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
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error
503Service UnavailableTemporary outage

Error Response Format

All errors follow a simple JSON structure with status and message fields:

{
"status": 400,
"message": "\"email\" is required"
}

Error Object Fields

FieldTypeDescription
statusnumberHTTP status code
messagestringHuman-readable error description

Some error responses may include additional fields depending on the error type.

Error Types

Authentication Errors

When API key authentication fails:

{
"status": 401,
"message": "API key is required. Provide it via tt-api-key header or Authorization: Bearer header"
}

Invalid API key format:

{
"status": 401,
"message": "API key must start with tt_ prefix"
}

Revoked or invalid API key:

{
"status": 401,
"message": "API key not found or has been revoked"
}

Expired API key:

{
"status": 401,
"message": "API key has expired"
}

Authorization Errors

Insufficient permissions:

{
"status": 403,
"message": "API key does not have access to PRODUCTS"
}

Missing required action:

{
"status": 403,
"message": "API key cannot perform WRITE on PRODUCTS"
}

IP not whitelisted:

{
"status": 403,
"message": "Your IP address is not authorized to use this API key"
}

Validation Errors

When request validation fails:

{
"status": 400,
"message": "\"email\" must be a valid email"
}
{
"status": 400,
"message": "\"price\" must be greater than or equal to 0"
}

Not Found Errors

When a resource or route doesn't exist:

{
"name": "Resource not found",
"message": "The page you are trying to access does not exist"
}

Rate Limit Errors

When rate limit is exceeded:

{
"message": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"status": 429
}

Server Errors

When an internal server error occurs:

{
"status": 500,
"message": "INTERNAL_SERVER_ERROR"
}

Duplicate Entry Errors

When attempting to create a resource that already exists:

{
"status": 400,
"message": "slug and storeId combination with my-product and 65abc123def already exists."
}

Handling Errors

JavaScript/TypeScript

interface ApiError {
status: number;
message: string;
code?: string;
name?: 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 as ApiError;

switch (response.status) {
case 401:
throw new AuthenticationError(error.message);
case 403:
throw new ForbiddenError(error.message);
case 404:
throw new NotFoundError(error.message || error.name);
case 429:
throw new RateLimitError(error.message);
default:
throw new ApiError(error.message, error.status);
}
}

return data as T;
}

// Usage
try {
const product = await apiRequest("/products/prod_123");
} catch (error) {
if (error instanceof RateLimitError) {
// Wait and retry
await sleep(60000);
// Retry request...
} else if (error instanceof NotFoundError) {
console.log("Product not found");
} else if (error instanceof AuthenticationError) {
console.log("Check your API key");
} else {
console.error("Unexpected error:", error.message);
}
}

Python

import requests
from typing import Optional

class ApiError(Exception):
def __init__(self, message: str, status: int):
super().__init__(message)
self.message = message
self.status = status

class AuthenticationError(ApiError):
pass

class ForbiddenError(ApiError):
pass

class NotFoundError(ApiError):
pass

class RateLimitError(ApiError):
pass

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': API_KEY,
'Content-Type': 'application/json'
},
json=data
)

result = response.json()

if not response.ok:
message = result.get('message', result.get('name', 'Unknown error'))
status = result.get('status', response.status_code)

if response.status_code == 401:
raise AuthenticationError(message, status)
elif response.status_code == 403:
raise ForbiddenError(message, status)
elif response.status_code == 404:
raise NotFoundError(message, status)
elif response.status_code == 429:
raise RateLimitError(message, status)
else:
raise ApiError(message, status)

return result

# Usage
try:
product = api_request('/products/prod_123')
except RateLimitError as e:
import time
time.sleep(60)
# Retry request...
except NotFoundError as e:
print("Product not found")
except AuthenticationError as e:
print("Check your API key")
except ApiError as e:
print(f"Error: {e.message} (Status: {e.status})")

Error Codes Reference

Authentication & Authorization

Error MessageStatusDescription
API key is required...401No API key provided
API key must start with tt_ prefix401Invalid API key format
API key not found or has been revoked401API key doesn't exist or was revoked
API key has expired401API key past expiration date
API key usage limit has been reached429Usage quota exceeded
API key does not have access to [RESOURCE]403Missing resource scope
API key cannot perform [ACTION] on [RESOURCE]403Missing action permission
Your IP address is not authorized...403IP not in whitelist

Rate Limiting

Error MessageCodeStatusDescription
Rate limit exceededRATE_LIMIT_EXCEEDED429Too many requests

Validation

Validation error messages are generated by the request schema and describe exactly which field failed and why. Common patterns include:

PatternDescription
"[field]" is requiredRequired field is missing
"[field]" must be a valid emailInvalid email format
"[field]" must be greater than [value]Value below minimum
"[field]" must be less than [value]Value exceeds maximum
"[field]" must be one of [values]Invalid enum value
"[field]" must be a stringWrong data type

Resources

Error MessageStatusDescription
The page you are trying to access does not exist404Route or resource not found
[field] combination with [values] already exists.400Duplicate entry

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) {
const error = await response.json();
console.error("Error:", error.message);
return;
}
const { data } = await response.json();
renderProducts(data);

2. Handle Different Status Codes

async function handleApiError(response) {
const error = await response.json();

switch (response.status) {
case 400:
return `Validation error: ${error.message}`;
case 401:
return `Authentication failed: ${error.message}`;
case 403:
return `Permission denied: ${error.message}`;
case 404:
return "Resource not found";
case 429:
return "Rate limit exceeded. Please wait before retrying.";
case 500:
return "Server error. Please try again later.";
default:
return error.message || "An unexpected error occurred";
}
}

3. Display User-Friendly Messages

function getUserMessage(error) {
// Don't expose internal error details to users
if (error.status >= 500) {
return "Something went wrong. Please try again later.";
}

if (error.status === 429) {
return "Too many requests. Please wait a moment.";
}

// For client errors, the message is usually safe to show
return error.message;
}

4. Implement Retry Logic for Transient Errors

async function fetchWithRetry(endpoint, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await apiRequest(endpoint);
return response;
} catch (error) {
// Only retry on server errors or rate limits
if (error.status !== 500 && error.status !== 503 && error.status !== 429) {
throw error; // Don't retry client errors
}

// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error("Max retries exceeded");
}