Skip to main content

Overview

The Vepler API uses standard HTTP status codes and returns detailed error objects to help you handle errors gracefully. All errors follow a consistent structure for easy parsing and debugging.

Error Response Structure

All API errors return a JSON response with the following structure:
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Property with ID 'p_0x999999' not found",
    "details": {
      "resource": "property",
      "id": "p_0x999999"
    },
    "request_id": "req_1234567890",
    "timestamp": "2024-03-20T14:30:00Z"
  }
}

Error Fields

FieldTypeDescription
codestringMachine-readable error code
messagestringHuman-readable error message
detailsobjectAdditional context about the error
request_idstringUnique request identifier for support
timestampstringISO 8601 timestamp of the error

HTTP Status Codes

Success Codes (2xx)

StatusDescriptionWhen Used
200 OKRequest succeededSuccessful GET, PUT, DELETE
201 CreatedResource createdSuccessful POST creating new resource
204 No ContentRequest succeeded, no contentSuccessful DELETE

Client Error Codes (4xx)

StatusError CodeDescription
400 Bad RequestINVALID_REQUESTMalformed request syntax or invalid parameters
401 UnauthorizedUNAUTHORIZEDMissing or invalid API key
403 ForbiddenFORBIDDENValid API key but insufficient permissions
404 Not FoundRESOURCE_NOT_FOUNDRequested resource doesn’t exist
409 ConflictCONFLICTRequest conflicts with current state
422 Unprocessable EntityVALIDATION_ERRORRequest body validation failed
429 Too Many RequestsRATE_LIMIT_EXCEEDEDRate limit exceeded

Server Error Codes (5xx)

StatusError CodeDescription
500 Internal Server ErrorINTERNAL_ERRORUnexpected server error
502 Bad GatewayGATEWAY_ERRORUpstream service error
503 Service UnavailableSERVICE_UNAVAILABLEService temporarily unavailable
504 Gateway TimeoutGATEWAY_TIMEOUTUpstream service timeout

Common Error Codes

Authentication Errors

{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The provided API key is invalid",
    "details": {
      "header": "x-api-key",
      "key_prefix": "vpr_test_"
    }
  }
}
CodeDescriptionSolution
MISSING_API_KEYNo API key providedInclude x-api-key header
INVALID_API_KEYAPI key is malformed or doesn’t existCheck your API key
EXPIRED_API_KEYAPI key has expiredGenerate a new API key
INSUFFICIENT_PERMISSIONSAPI key lacks required permissionsUpgrade plan or request access

Validation Errors

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "fields": [
        {
          "field": "latitude",
          "message": "Must be between -90 and 90",
          "value": 150
        },
        {
          "field": "radius",
          "message": "Must be positive number",
          "value": -100
        }
      ]
    }
  }
}
CodeDescriptionSolution
VALIDATION_ERRORRequest parameters invalidCheck field requirements
INVALID_DATE_FORMATDate format incorrectUse ISO 8601 format
INVALID_COORDINATESLat/lng out of rangeVerify coordinate values
INVALID_POSTCODEUK postcode invalidCheck postcode format

Resource Errors

{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Property not found",
    "details": {
      "resource": "property",
      "id": "p_0x999999",
      "suggestions": [
        "p_0x999998",
        "p_0x999997"
      ]
    }
  }
}
CodeDescriptionSolution
RESOURCE_NOT_FOUNDResource doesn’t existVerify resource ID
RESOURCE_DELETEDResource has been deletedResource no longer available
RESOURCE_LOCKEDResource temporarily lockedRetry after delay

Rate Limiting Errors

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "API rate limit exceeded",
    "details": {
      "limit": 1000,
      "remaining": 0,
      "reset_at": "2024-03-20T15:00:00Z",
      "retry_after": 1800
    }
  }
}
CodeDescriptionSolution
RATE_LIMIT_EXCEEDEDToo many requestsWait for reset or upgrade
DAILY_QUOTA_EXCEEDEDDaily limit reachedWait until tomorrow or upgrade
CONCURRENT_LIMIT_EXCEEDEDToo many concurrent requestsReduce parallel requests

Query Errors

{
  "error": {
    "code": "QUERY_TOO_BROAD",
    "message": "Query would return too many results",
    "details": {
      "estimated_results": 50000,
      "max_allowed": 10000,
      "suggestion": "Add more filters to narrow results"
    }
  }
}
CodeDescriptionSolution
QUERY_TOO_BROADQuery returns too many resultsAdd more filters
INVALID_QUERY_SYNTAXQuery syntax incorrectCheck query format
UNSUPPORTED_FILTERFilter not supportedRemove unsupported filter

Error Handling Examples

TypeScript/JavaScript

import { Vepler } from 'vepler-sdk';

const vepler = new Vepler({ apiKey: process.env.VEPLER_API_KEY });

async function getPropertySafely(id: string) {
  try {
    const property = await vepler.property.get(id);
    return property.data;
  } catch (error) {
    if (error.status === 404) {
      console.log('Property not found');
      return null;
    }

    if (error.status === 429) {
      const retryAfter = error.details?.retry_after || 60;
      console.log(`Rate limited. Retry after ${retryAfter} seconds`);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      return getPropertySafely(id); // Retry
    }

    if (error.status === 401) {
      console.error('Invalid API key');
      throw new Error('Authentication failed');
    }

    // Log unexpected errors
    console.error('Unexpected error:', error);
    throw error;
  }
}

Python

import time
from vepler import Vepler, VeplerError

client = Vepler(api_key=os.environ['VEPLER_API_KEY'])

def get_property_safely(property_id):
    try:
        property = client.property.get(property_id)
        return property.data
    except VeplerError as e:
        if e.status_code == 404:
            print(f"Property {property_id} not found")
            return None

        elif e.status_code == 429:
            retry_after = e.details.get('retry_after', 60)
            print(f"Rate limited. Waiting {retry_after} seconds...")
            time.sleep(retry_after)
            return get_property_safely(property_id)  # Retry

        elif e.status_code == 401:
            raise Exception("Invalid API key")

        else:
            print(f"Unexpected error: {e}")
            raise

cURL

#!/bin/bash

response=$(curl -s -w "\n%{http_code}" \
  -H "x-api-key: $VEPLER_API_KEY" \
  "https://api.vepler.com/v1/property/p_0x000123456789")

body=$(echo "$response" | head -n -1)
status=$(echo "$response" | tail -n 1)

if [ "$status" -eq 200 ]; then
  echo "Success: $body"
elif [ "$status" -eq 404 ]; then
  echo "Property not found"
elif [ "$status" -eq 429 ]; then
  retry_after=$(echo "$body" | jq -r '.error.details.retry_after')
  echo "Rate limited. Retry after $retry_after seconds"
  sleep "$retry_after"
elif [ "$status" -eq 401 ]; then
  echo "Authentication failed"
  exit 1
else
  echo "Error $status: $body"
  exit 1
fi

Retry Strategy

Implement exponential backoff for transient errors:
async function retryWithBackoff(
  fn: () => Promise<any>,
  maxRetries = 3,
  baseDelay = 1000
) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      // Don't retry client errors (except 429)
      if (error.status >= 400 && error.status < 500 && error.status !== 429) {
        throw error;
      }

      if (i === maxRetries - 1) throw error;

      const delay = baseDelay * Math.pow(2, i);
      console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Usage
const property = await retryWithBackoff(() =>
  vepler.property.get('p_0x000123456789')
);

Error Recovery Patterns

Circuit Breaker

Prevent cascading failures:
class CircuitBreaker {
  private failures = 0;
  private lastFailTime = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';

  constructor(
    private threshold = 5,
    private timeout = 60000
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailTime > this.timeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }

    try {
      const result = await fn();
      if (this.state === 'HALF_OPEN') {
        this.state = 'CLOSED';
        this.failures = 0;
      }
      return result;
    } catch (error) {
      this.failures++;
      this.lastFailTime = Date.now();

      if (this.failures >= this.threshold) {
        this.state = 'OPEN';
      }
      throw error;
    }
  }
}

Graceful Degradation

Provide fallback behavior:
async function getPropertyWithFallback(id: string) {
  try {
    // Try to get fresh data
    const property = await vepler.property.get(id);
    cache.set(id, property.data);
    return property.data;
  } catch (error) {
    // Fall back to cache if available
    const cached = cache.get(id);
    if (cached) {
      console.warn('Using cached data due to error:', error.message);
      return cached;
    }

    // Return partial data if appropriate
    if (error.status === 503) {
      return {
        id,
        status: 'unavailable',
        message: 'Property data temporarily unavailable'
      };
    }

    throw error;
  }
}

Debugging

Use the request_id for support requests:
try {
  const property = await vepler.property.get('p_0x000123456789');
} catch (error) {
  console.error(`Error (Request ID: ${error.request_id}):`, error.message);

  // When contacting support, provide:
  // 1. Request ID
  // 2. Timestamp
  // 3. Error code
  // 4. Endpoint called
}

Best Practices

Never assume API calls will succeed:
// ❌ Bad
const property = await vepler.property.get(id);
console.log(property.address);

// ✅ Good
try {
  const property = await vepler.property.get(id);
  console.log(property.address);
} catch (error) {
  handleError(error);
}
Include context in error logs:
logger.error('Property fetch failed', {
  propertyId: id,
  errorCode: error.code,
  errorMessage: error.message,
  requestId: error.request_id,
  timestamp: new Date().toISOString()
});
Handle different errors appropriately:
function handleApiError(error) {
  switch (error.code) {
    case 'RESOURCE_NOT_FOUND':
      return { found: false };
    case 'RATE_LIMIT_EXCEEDED':
      return scheduleRetry(error.details.retry_after);
    case 'VALIDATION_ERROR':
      return { valid: false, errors: error.details.fields };
    default:
      throw error;
  }
}
Set reasonable timeouts:
const property = await Promise.race([
  vepler.property.get(id),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 5000)
  )
]);

Next Steps