> ## Documentation Index
> Fetch the complete documentation index at: https://docs.screenshotly.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Error codes

> HTTP status codes and error response format for the Screenshotly API.

The Screenshotly API uses standard HTTP status codes to indicate success or failure.

## Status code overview

| Range   | Meaning                                  |
| ------- | ---------------------------------------- |
| **2xx** | The request succeeded                    |
| **4xx** | The request failed due to a client error |
| **5xx** | The request failed due to a server error |

## Error response format

All error responses return a JSON body with an `error` object containing `code` and `message` fields:

```json theme={null}
{
  "error": {
    "code": "invalid_options",
    "message": "Validation failed."
  }
}
```

Some errors include a `details` field with additional context:

```json theme={null}
{
  "error": {
    "code": "invalid_options",
    "message": "Validation failed.",
    "details": {
      "url": "\"url\" is required"
    }
  }
}
```

## Error codes

| Status  | Code                   | Description                                                                                                                 |
| ------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **401** | `missing_api_key`      | No API key provided. Pass it via the `X-API-Key` header or `api_key` query parameter.                                       |
| **401** | `invalid_api_key`      | The provided API key is invalid or has been revoked.                                                                        |
| **401** | `unauthorized`         | Authentication required. Provide a valid API key.                                                                           |
| **402** | `usage_limit_exceeded` | Monthly screenshot limit reached. Upgrade your subscription at the [dashboard](https://app.screenshotly.dev).               |
| **403** | `forbidden`            | You do not have permission to access this resource.                                                                         |
| **404** | `not_found`            | The requested resource (e.g., screenshot ID) was not found.                                                                 |
| **422** | `invalid_options`      | Validation failed. Check the `details` field for specific parameter errors.                                                 |
| **422** | `invalid_url`          | The provided URL is not valid. Ensure it includes a protocol (e.g., `https://`).                                            |
| **422** | `selector_not_found`   | The specified CSS `selector` was not found on the page. Only returned when `error_on_selector_not_found` is `true`.         |
| **429** | `rate_limit_exceeded`  | Too many requests per minute. See [rate limits](/api-reference/rate-limits).                                                |
| **500** | `render_failed`        | Screenshot rendering failed. The target page may have caused an error.                                                      |
| **500** | `internal_error`       | An unexpected server error occurred. Retry the request. If it persists, [contact support](mailto:support@screenshotly.dev). |
| **502** | `host_error`           | Could not reach the target URL. The site may be down or blocking requests.                                                  |
| **503** | `service_unavailable`  | The screenshot service is starting up. Retry in a few seconds.                                                              |
| **504** | `navigation_timeout`   | Page navigation timed out. Try increasing the `navigation_timeout` or `timeout` options.                                    |

## Handling errors

### Python

```python theme={null}
import requests

response = requests.post(
    'https://api.screenshotly.dev/v1/capture',
    headers={'X-API-Key': API_KEY, 'Content-Type': 'application/json'},
    json={'url': 'https://example.com', 'options': {'response_type': 'json'}}
)

if response.status_code == 401:
    print('Check your API key')
elif response.status_code == 402:
    print('Monthly limit reached — upgrade your plan')
elif response.status_code == 429:
    print('Rate limited — retry after a delay')
elif response.status_code >= 400:
    error = response.json()['error']
    print(f"Error [{error['code']}]: {error['message']}")
```

### JavaScript

```javascript theme={null}
const response = await fetch('https://api.screenshotly.dev/v1/capture', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.SCREENSHOTLY_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com',
    options: { response_type: 'json' },
  }),
});

if (!response.ok) {
  const { error } = await response.json();
  console.error(`Error [${error.code}]: ${error.message}`);

  if (error.details) {
    console.error('Details:', error.details);
  }
}
```

## Retry strategy

For transient errors (`429`, `5xx`), implement exponential backoff:

```javascript theme={null}
async function captureWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch('https://api.screenshotly.dev/v1/capture', {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.SCREENSHOTLY_API_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url, options }),
    });

    if (response.ok) return response;

    // Don't retry client errors (except 429)
    if (response.status >= 400 && response.status < 500 && response.status !== 429) {
      const { error } = await response.json();
      throw new Error(`${error.code}: ${error.message}`);
    }

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

  throw new Error('Max retries exceeded');
}
```
