Skip to main content

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 RangeMeaning
2xxSuccess — The request completed successfully
4xxClient Error — There's a problem with your request
5xxServer Error — Something went wrong on our end

Common Status Codes

CodeStatusDescription
200OKRequest succeeded
201CreatedResource created successfully
204No ContentRequest succeeded, no content returned
400Bad RequestInvalid request parameters
401UnauthorizedMissing or invalid authentication
403ForbiddenInsufficient permissions
404Not FoundResource doesn't exist
409ConflictResource state conflict
422Unprocessable EntityValidation error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error
503Service UnavailableTemporary 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

FieldTypeDescription
typestringError category (e.g., validation_error, not_found)
messagestringHuman-readable error description
codestringMachine-readable error code
statusnumberHTTP status code
detailsarrayField-specific errors (for validation)
request_idstringUnique 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

CodeDescription
INVALID_API_KEYAPI key is malformed or doesn't exist
EXPIRED_API_KEYAPI key has been revoked or expired
INSUFFICIENT_PERMISSIONSAPI key lacks required scopes
IP_NOT_ALLOWEDRequest from non-allowlisted IP

Validation

CodeDescription
VALIDATION_FAILEDOne or more fields failed validation
REQUIREDRequired field is missing
INVALID_FORMATField format is incorrect
MIN_VALUEValue is below minimum
MAX_VALUEValue exceeds maximum
INVALID_ENUMValue not in allowed list

Resources

CodeDescription
PRODUCT_NOT_FOUNDProduct doesn't exist
ORDER_NOT_FOUNDOrder doesn't exist
CUSTOMER_NOT_FOUNDCustomer doesn't exist
RESOURCE_CONFLICTResource state conflict
DUPLICATE_ENTRYResource already exists

Rate Limiting

CodeDescription
RATE_LIMIT_EXCEEDEDToo many requests
QUOTA_EXCEEDEDMonthly 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");
}