Skip to main content

Error Handling

Learn how to handle API errors effectively.

Error Response Format​

All errors follow this structure:

{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
],
"request_id": "req_abc123"
}
}

HTTP Status Codes​

CodeMeaningDescription
400Bad RequestInvalid request format or parameters
401UnauthorizedMissing or invalid authentication
403ForbiddenInsufficient permissions
404Not FoundResource doesn't exist
409ConflictResource conflict (duplicate)
422Unprocessable EntityValidation failed
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error
503Service UnavailableTemporary unavailability

Error Codes​

Authentication Errors​

CodeHTTPDescription
UNAUTHORIZED401Missing or invalid token
TOKEN_EXPIRED401Access token has expired
INVALID_API_KEY401API key is invalid
API_KEY_REVOKED401API key has been revoked

Authorization Errors​

CodeHTTPDescription
FORBIDDEN403Access denied
INSUFFICIENT_PERMISSIONS403Missing required scope
WORKSPACE_ACCESS_DENIED403No access to workspace

Validation Errors​

CodeHTTPDescription
VALIDATION_ERROR422Input validation failed
INVALID_FORMAT400Invalid data format
MISSING_REQUIRED_FIELD422Required field missing
INVALID_FIELD_VALUE422Field value invalid

Resource Errors​

CodeHTTPDescription
NOT_FOUND404Resource not found
ALREADY_EXISTS409Resource already exists
CONFLICT409Resource conflict
GONE410Resource deleted

Rate Limit Errors​

CodeHTTPDescription
RATE_LIMIT_EXCEEDED429Rate limit exceeded
DAILY_LIMIT_EXCEEDED429Daily quota exceeded

Server Errors​

CodeHTTPDescription
INTERNAL_ERROR500Internal server error
SERVICE_UNAVAILABLE503Service temporarily unavailable
TIMEOUT504Request timeout

Handling Errors​

TypeScript Example​

interface ApiError {
code: string;
message: string;
details?: Array<{ field: string; message: string }>;
request_id: string;
}

interface ApiResponse<T> {
success: boolean;
data?: T;
error?: ApiError;
}

async function apiRequest<T>(url: string, options?: RequestInit): Promise<T> {
const response = await fetch(url, {
...options,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
...options?.headers,
},
});

const result: ApiResponse<T> = await response.json();

if (!result.success) {
throw new ApiRequestError(result.error!, response.status);
}

return result.data!;
}

class ApiRequestError extends Error {
constructor(
public error: ApiError,
public statusCode: number
) {
super(error.message);
this.name = 'ApiRequestError';
}

get isValidationError() {
return this.error.code === 'VALIDATION_ERROR';
}

get isAuthError() {
return this.statusCode === 401;
}

get isRateLimited() {
return this.statusCode === 429;
}
}

// Usage
try {
const lead = await apiRequest('/v1/leads/lead_123');
} catch (error) {
if (error instanceof ApiRequestError) {
if (error.isAuthError) {
// Refresh token or re-authenticate
} else if (error.isRateLimited) {
// Wait and retry
} else if (error.isValidationError) {
// Show validation errors to user
console.log(error.error.details);
}
}
}

Python Example​

import requests
from dataclasses import dataclass
from typing import Optional, List, Dict, Any

@dataclass
class ApiError:
code: str
message: str
details: Optional[List[Dict[str, str]]] = None
request_id: Optional[str] = None

class ApiException(Exception):
def __init__(self, error: ApiError, status_code: int):
self.error = error
self.status_code = status_code
super().__init__(error.message)

def api_request(method: str, url: str, **kwargs) -> Dict[str, Any]:
response = requests.request(
method,
f"https://api.propertybase.ai/v1{url}",
headers={"Authorization": f"Bearer {API_KEY}"},
**kwargs
)

result = response.json()

if not result.get("success"):
error_data = result.get("error", {})
raise ApiException(
ApiError(**error_data),
response.status_code
)

return result.get("data")

# Usage
try:
lead = api_request("GET", "/leads/lead_123")
except ApiException as e:
if e.status_code == 401:
# Handle authentication error
pass
elif e.status_code == 429:
# Handle rate limit
pass
elif e.error.code == "VALIDATION_ERROR":
# Handle validation errors
for detail in e.error.details or []:
print(f"{detail['field']}: {detail['message']}")

Validation Error Details​

Validation errors include detailed field-level messages:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "price.amount",
"message": "Price must be a positive number"
},
{
"field": "bedrooms",
"message": "Must be between 1 and 20"
}
]
}
}

Debugging​

Request ID​

Every response includes a request ID for debugging:

X-Request-Id: req_abc123def456

Include this ID when contacting support.

Error Logging​

Log errors for debugging:

function logApiError(error: ApiRequestError) {
console.error('API Error:', {
code: error.error.code,
message: error.error.message,
requestId: error.error.request_id,
statusCode: error.statusCode,
timestamp: new Date().toISOString(),
});
}

Best Practices​

  1. Always check success - Don't assume requests succeed
  2. Handle specific errors - Different errors need different handling
  3. Log request IDs - Essential for debugging
  4. Show user-friendly messages - Don't expose raw error messages
  5. Implement retries - For transient errors (500, 503, 429)
  6. Validate locally first - Reduce API calls for invalid data