---
title: "Error Handling"
url: "https://adaptocms.com/docs/error-handling/"
source: adaptocms.com
---

# Error Handling

The Adapto CMS Public API uses standard HTTP status codes and returns structured error responses. This page covers all error scenarios and how to handle them in your applications.

## HTTP Status Codes

Code

Meaning

When It Occurs

`200`

OK

Request succeeded. Response body contains the requested data.

`401`

Unauthorized

API key is missing from the request headers.

`403`

Forbidden

API key is invalid or does not have access to the tenant.

`404`

Not Found

The requested resource (article, page, category, collection, or item) does not exist.

`422`

Validation Error

Request parameters failed validation (wrong type, missing required param, etc.).

`429`

Too Many Requests

Rate limit exceeded. Back off and retry.

`500`

Internal Server Error

Unexpected server error. Contact support if persistent.

* * *

## Validation Errors (422)

When query parameters or path parameters fail validation, the API returns a `422 Unprocessable Entity` response with detailed error information:

```
{
  "detail": [
    {
      "loc": ["query", "limit"],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ]
}
```

### Error Fields

Field

Type

Description

`loc`

array

Location path indicating where the error occurred (e.g. `["query", "limit"]`, `["path", "article_id"]`)

`msg`

string

Human-readable error message

`type`

string

Machine-readable error type identifier

### Multiple Validation Errors

Multiple errors can be returned at once:

```
{
  "detail": [
    {
      "loc": ["query", "page"],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    },
    {
      "loc": ["query", "limit"],
      "msg": "ensure this value is greater than 0",
      "type": "value_error.number.not_gt"
    }
  ]
}
```

* * *

## Not Found Errors (404)

When a resource is not found by ID or slug:

```
{
  "detail": "Article not found"
}
```

* * *

## Error Handling Best Practices

### JavaScript / TypeScript

```
async function fetchArticle(slug) {
  const response = await fetch(
    `${BASE_URL}/v1/articles/by-slug/${slug}`,
    { headers: { 'x-api-key': API_KEY } }
  );

  if (!response.ok) {
    switch (response.status) {
      case 401:
      case 403:
        throw new Error('Authentication failed. Check your API key.');
      case 404:
        return null; // Article not found
      case 422:
        const errors = await response.json();
        throw new Error(`Validation error: ${errors.detail.map(e => e.msg).join(', ')}`);
      case 429:
        // Implement retry with exponential backoff
        await sleep(1000);
        return fetchArticle(slug);
      default:
        throw new Error(`API error: ${response.status}`);
    }
  }

  return response.json();
}
```

### Python

```
import requests
from time import sleep

def fetch_article(slug, retries=3):
    response = requests.get(
        f'{BASE_URL}/v1/articles/by-slug/{slug}',
        headers={'x-api-key': API_KEY}
    )

    if response.status_code == 200:
        return response.json()
    elif response.status_code == 404:
        return None
    elif response.status_code == 422:
        errors = response.json()
        raise ValueError(f"Validation error: {errors['detail']}")
    elif response.status_code == 429 and retries > 0:
        sleep(1)
        return fetch_article(slug, retries - 1)
    else:
        response.raise_for_status()
```

* * *

## Rate Limiting

The Public API enforces rate limits based on your subscription plan. When you exceed the limit, requests return `429 Too Many Requests`. Implement exponential backoff in your client:

```
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) return response;

    const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error('Rate limit exceeded after retries');
}
```
