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

# Errors

> Standard error codes and how to handle them.

## Error format

All errors return a consistent JSON shape:

```json theme={null}
{
  "error": {
    "code": "BAD_REQUEST",
    "message": "Human-readable description of the problem"
  }
}
```

## Error codes

| Code | HTTP Status | Description |
| - | - | - |
| `BAD_REQUEST` | 400 | Invalid request body or parameters |
| `UNAUTHORIZED` | 401 | Missing or invalid API key |
| `FORBIDDEN` | 403 | Valid key but not authorized for this resource |
| `NOT_FOUND` | 404 | Document or resource does not exist |
| `CONFLICT` | 409 | Resource state conflict |
| `SCHEMA_VALIDATION_FAILED` | 422 | Request was well-formed but semantically invalid |
| `RATE_LIMITED` | 429 | Too many requests - see [rate limits](/api-reference/rate-limits) |
| `INTERNAL_ERROR` | 500 | Unexpected server error |
| `TIMEOUT` | 504 | The operation timed out |

## Handling errors

### Retry strategy

For transient errors (`429`, `500`, `502`, `503`, `504`), use exponential backoff:

```python theme={null}
import time
import requests

def api_call_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers)

        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 5))
            time.sleep(retry_after)
            continue

        if resp.status_code >= 500:
            time.sleep(2 ** attempt)
            continue

        return resp

    raise Exception(f"Failed after {max_retries} retries")
```

### Non-retryable errors

| Code | Action |
| - | - |
| `400` | Fix your request body or parameters |
| `401` | Check your API key |
| `403` | Verify you own the resource |
| `404` | Verify the document ID exists |
| `422` | Check request schema against the API reference |

## Structured output errors

The `/v1/documents/{id}/structured-output` endpoint returns specific extraction errors:

| Code | HTTP Status | Description |
| - | - | - |
| `SCHEMA_VALIDATION_FAILED` | 422 | Extracted data didn't match your JSON schema. Check field types, required fields, and nesting. |
| `EXTRACTION_BLOCKED` | 422 | The model couldn't extract data — document has no pages, parsing failed, or all queries returned errors. |
| `TIMEOUT` | 504 | Extraction exceeded the time limit (default 45s, MCP uses 120s). Simplify the schema or use page ranges. |
| `DOCUMENT_NOT_FOUND` | 404 | Document ID does not exist or has not finished processing. Check document status first. |

### Example error response

```json theme={null}
{
  "error": {
    "message": "Structured output timed out",
    "type": "server_error",
    "details": { "timeoutMs": 120000 }
  }
}
```

### Tips for reliable extraction

* **Keep schemas flat** when possible — fewer nested objects means faster extraction
* **Use string types** for financial values (e.g. `"$215,938 million"`) rather than numbers to avoid parsing ambiguity
* **Check document status** before extracting — `phase: "complete"` is required


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.