Error Handling
The TakeTheme API uses conventional HTTP response codes to indicate the success or failure of requests. This guide covers error response formats and best practices for handling them.
HTTP Status Codes
| Code Range | Meaning |
|---|---|
2xx | Success — The request completed successfully |
4xx | Client Error — There's a problem with your request |
5xx | Server Error — Something went wrong on our end |
Common Status Codes
| Code | Status | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
204 | No Content | Request succeeded, no content returned |
400 | Bad Request | Invalid request parameters |
401 | Unauthorized | Missing or invalid authentication |
403 | Forbidden | Insufficient permissions |
404 | Not Found | Resource doesn't exist |
409 | Conflict | Resource state conflict |
422 | Unprocessable Entity | Validation error |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Server-side error |
503 | Service Unavailable | Temporary outage |
Error Response Format
All errors follow a consistent JSON structure:
{
"error": {
"type": "validation_error",
"message": "The provided data is invalid",
"code": "VALIDATION_FAILED",
"status": 422,
"details": [
{
"field": "email",
"message": "Email is required",
"code": "REQUIRED"
},
{
"field": "price",
"message": "Price must be greater than 0",
"code": "MIN_VALUE",
"min": 0
}
],
"request_id": "req_abc123xyz789"
}
}
Error Object Fields
| Field | Type | Description |
|---|---|---|
type | string | Error category (e.g., validation_error, not_found) |
message | string | Human-readable error description |
code | string | Machine-readable error code |
status | number | HTTP status code |
details | array | Field-specific errors (for validation) |
request_id | string | Unique request identifier for support |
Error Types
Authentication Errors
{
"error": {
"type": "unauthorized",
"message": "Invalid API key provided",
"code": "INVALID_API_KEY",
"status": 401
}
}
Validation Errors
{
"error": {
"type": "validation_error",
"message": "Request validation failed",
"code": "VALIDATION_FAILED",
"status": 422,
"details": [
{
"field": "variants[0].price",
"message": "Price is required for each variant",
"code": "REQUIRED"
}
]
}
}
Not Found Errors
{
"error": {
"type": "not_found",
"message": "Product not found",
"code": "PRODUCT_NOT_FOUND",
"status": 404,
"resource": "product",
"resource_id": "prod_xyz123"
}
}
Rate Limit Errors
{
"error": {
"type": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 45 seconds.",
"code": "RATE_LIMIT_EXCEEDED",
"status": 429,
"retry_after": 45
}
}
Server Errors
{
"error": {
"type": "internal_error",
"message": "An unexpected error occurred. Please try again.",
"code": "INTERNAL_ERROR",
"status": 500,
"request_id": "req_abc123xyz789"
}
}
Handling Errors
JavaScript/TypeScript
interface ApiError {
type: string;
message: string;
code: string;
status: number;
details?: Array<{
field: string;
message: string;
code: string;
}>;
request_id?: string;
}
async function apiRequest<T>(
endpoint: string,
options?: RequestInit
): Promise<T> {
const response = await fetch(`https://api.taketheme.com/api/v1${endpoint}`, {
...options,
headers: {
tt-api-key: ` ${API_KEY}`,
"Content-Type": "application/json",
...options?.headers,
},
});
const data = await response.json();
if (!response.ok) {
const error = data.error as ApiError;
switch (error.status) {
case 401:
throw new AuthenticationError(error.message);
case 404:
throw new NotFoundError(error.message);
case 422:
throw new ValidationError(error.message, error.details);
case 429:
throw new RateLimitError(error.message, error.retry_after);
default:
throw new ApiError(error.message, error.code, error.status);
}
}
return data as T;
}
// Usage
try {
const product = await apiRequest("/products/prod_123");
} catch (error) {
if (error instanceof ValidationError) {
error.details.forEach((detail) => {
console.log(`${detail.field}: ${detail.message}`);
});
} else if (error instanceof RateLimitError) {
await sleep(error.retryAfter * 1000);
// Retry request...
} else if (error instanceof NotFoundError) {
console.log("Product not found");
} else {
console.error("Unexpected error:", error.message);
}
}
Python
import requests
from typing import Optional, List, Dict, Any
class ApiError(Exception):
def __init__(self, message: str, code: str, status: int, request_id: Optional[str] = None):
super().__init__(message)
self.code = code
self.status = status
self.request_id = request_id
class ValidationError(ApiError):
def __init__(self, message: str, details: List[Dict[str, Any]]):
super().__init__(message, 'VALIDATION_FAILED', 422)
self.details = details
class RateLimitError(ApiError):
def __init__(self, message: str, retry_after: int):
super().__init__(message, 'RATE_LIMIT_EXCEEDED', 429)
self.retry_after = retry_after
def api_request(endpoint: str, method: str = 'GET', data: Optional[dict] = None) -> dict:
response = requests.request(
method,
f'https://api.taketheme.com/api/v1{endpoint}',
headers={
'tt-api-key': f'{API_KEY}',
'Content-Type': 'application/json'
},
json=data
)
result = response.json()
if not response.ok:
error = result['error']
if error['status'] == 422:
raise ValidationError(error['message'], error.get('details', []))
elif error['status'] == 429:
raise RateLimitError(error['message'], error.get('retry_after', 60))
else:
raise ApiError(
error['message'],
error['code'],
error['status'],
error.get('request_id')
)
return result
# Usage
try:
product = api_request('/products/prod_123')
except ValidationError as e:
for detail in e.details:
print(f"{detail['field']}: {detail['message']}")
except RateLimitError as e:
import time
time.sleep(e.retry_after)
# Retry request...
except ApiError as e:
print(f"Error: {e.message} (Code: {e.code})")
Error Codes Reference
Authentication & Authorization
| Code | Description |
|---|---|
INVALID_API_KEY | API key is malformed or doesn't exist |
EXPIRED_API_KEY | API key has been revoked or expired |
INSUFFICIENT_PERMISSIONS | API key lacks required scopes |
IP_NOT_ALLOWED | Request from non-allowlisted IP |
Validation
| Code | Description |
|---|---|
VALIDATION_FAILED | One or more fields failed validation |
REQUIRED | Required field is missing |
INVALID_FORMAT | Field format is incorrect |
MIN_VALUE | Value is below minimum |
MAX_VALUE | Value exceeds maximum |
INVALID_ENUM | Value not in allowed list |
Resources
| Code | Description |
|---|---|
PRODUCT_NOT_FOUND | Product doesn't exist |
ORDER_NOT_FOUND | Order doesn't exist |
CUSTOMER_NOT_FOUND | Customer doesn't exist |
RESOURCE_CONFLICT | Resource state conflict |
DUPLICATE_ENTRY | Resource already exists |
Rate Limiting
| Code | Description |
|---|---|
RATE_LIMIT_EXCEEDED | Too many requests |
QUOTA_EXCEEDED | Monthly quota exceeded |
Best Practices
1. Always Check for Errors
// ✗ Bad: Ignoring potential errors
const { data } = await fetch("/products");
renderProducts(data);
// ✓ Good: Handling errors explicitly
const response = await fetch("/products");
if (!response.ok) {
handleError(await response.json());
return;
}
const { data } = await response.json();
renderProducts(data);
2. Log Request IDs
catch (error) {
console.error('API Error:', {
message: error.message,
code: error.code,
requestId: error.requestId // Include in support tickets
});
}
3. Display User-Friendly Messages
function getUserMessage(error) {
switch (error.code) {
case "VALIDATION_FAILED":
return "Please check your input and try again.";
case "RATE_LIMIT_EXCEEDED":
return "Too many requests. Please wait a moment.";
case "INTERNAL_ERROR":
return "Something went wrong. Please try again later.";
default:
return error.message;
}
}
4. Implement Retry Logic for Transient Errors
const RETRYABLE_CODES = ["INTERNAL_ERROR", "SERVICE_UNAVAILABLE"];
async function fetchWithRetry(endpoint, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await apiRequest(endpoint);
} catch (error) {
if (!RETRYABLE_CODES.includes(error.code)) {
throw error; // Don't retry non-transient errors
}
await sleep(Math.pow(2, attempt) * 1000);
}
}
throw new Error("Max retries exceeded");
}