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

# Errors

> Read API errors, fix invalid requests, and retry only when the response says to.

When a request fails, read `error.code` for the type of failure and `error.reason` for its specific cause. Use `error.param` to find an invalid input, and keep `meta.request_id` when asking for support.

Retry only when another request can succeed. Invalid inputs need a correction, inactive subscriptions need reactivation, and expired cursors need a new first page.

<span id="the-envelope" />

## Read an error response

```json theme={null}
{
  "object": "error",
  "error": {
    "code": "not_found",
    "message": "No pick of the day is published yet.",
    "reason": "pick_not_released",
    "retry_at": "2026-08-29T14:00:00+00:00",
    "doc_url": "https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day"
  },
  "meta": { "request_id": "req_example", "cached": false, "cost": 1 }
}
```

| Field | Use it to |
| - | - |
| `error.code` | Choose the general error handling. Existing codes keep their meaning. |
| `error.reason` | Handle a more specific cause. It is omitted when the code is sufficient. |
| `error.message` | Log a readable explanation. Do not parse its wording. |
| `error.param` | Locate the input at fault, such as `cursor`, `body`, `traders[0]`, or `Idempotency-Key`. It is omitted when no single input is responsible. |
| `error.retry_at` | Schedule a retry for the recommended RFC 3339 instant, when present. |
| `error.doc_url` | Open the relevant documentation, when present. |
| `error.freshness` | Inspect the requested and measured ages for a freshness-ceiling failure. |
| `meta.request_id` | Identify the request to support. It matches `X-Request-Id`. |

New reasons can be added. If you do not recognize a reason, handle its code rather than assuming success.

<span id="the-11-codes" />

## Choose an action by code

| HTTP | `error.code` | Action |
| - | - | - |
| `400` | `bad_request` | Correct the input identified by `error.param` and `error.reason`. |
| `401` | `invalid_api_key` | Check the Bearer header and credential. See [Authentication](/authentication). |
| `402` | `subscription_required` | Reactivate Pro or Max at [Billing](https://0xinsider.com/billing). Stop scheduled retries. |
| `403` | `forbidden` | Contact [support](mailto:support@0xinsider.com) about the account or blocked IP. |
| `403` | `insufficient_scope` | Use a credential with the scope named in `WWW-Authenticate`. This applies to integration keys and OAuth tokens. |
| `404` | `not_found` | Read the reason; the resource may be absent, unpublished, or outside tracked coverage. |
| `405` | `bad_request` | Use a method from the `Allow` header. |
| `408` | `request_timeout` | A handler exceeded 30 seconds. For `GET` or `HEAD`, follow `Retry-After`; for a write, check its state first. |
| `409` | `bad_request` | Read the reason for a conflicting operation or an unmet freshness requirement. |
| `410` | `not_found` | An export has expired. Submit a new export. |
| `413` | `bad_request` | Reduce the body below the 1,048,576-byte limit. |
| `415` | `bad_request` | Send the body with `Content-Type: application/json`. |
| `422` | `bad_request` | Correct a reused `Idempotency-Key` or fix the webhook verification challenge, as identified by `error.param`. |
| `423` | `account_locked` | Contact [support](mailto:support@0xinsider.com). |
| `429` | `rate_limited` | Follow the reason and `Retry-After`. See [Rate limits](/rate-limits). |
| `500` | `internal_error` | For a read, retry once; if it repeats, send the request ID to support. For a write, check its state and reuse its supported idempotency key before retrying. |
| `503` | `rate_limit_unavailable` | A dependency or prepared result is unavailable. Follow `Retry-After`; this code does not mean you exceeded a limit. |

<span id="the-24-reasons" />

## Correct an input or credential

| `error.reason` | HTTP | Action |
| - | - | - |
| `invalid_query` | `400` | Correct the query parameter named by `error.param`. |
| `unknown_query_parameter` | `400` | Remove or correct an unknown parameter sent with `X-Query-Validation: strict`. The message lists accepted names. |
| `invalid_path` | `400` | Correct the path segment. |
| `invalid_body` | `400` | Correct the JSON body or required field. |
| `unsupported_media_type` | `415` | Add `Content-Type: application/json`. |
| `payload_too_large` | `413` | Split the work into smaller request bodies. |
| `method_not_allowed` | `405` | Use a method named in `Allow`. |
| `unknown_endpoint` | `404` | Read `GET /api/v1` for the route list and correct the URL. |
| `api_key_in_query` | `401` | Move the credential from `?token=` to the Bearer header. |
| `sandbox_api_key` | `401` | Use the sandbox URL, or use a live credential on production. |
| `subscription_inactive` | `402` | Reactivate Pro or Max. The same valid key can then resume working. |

## Restart or change the requested work

| `error.reason` | HTTP | Action |
| - | - | - |
| `cursor_expired` | `400` | Discard this pagination run and request the first page again. |
| `trader_not_tracked` | `404` | The position timeline is unavailable for this wallet. Stop timeline retries; the trader endpoint can still return `200` with `sync_status: unknown`. |
| `export_expired` | `410` | The completed export's retention window has ended. Submit a new export. |
| `freshness_ceiling_unsatisfied` | `409` | Read `error.freshness`; the stored trader response cannot meet your requested `max_age_s`. Change the age requirement or obtain a fresher result. |
| `webhook_secret_rotation_not_prepared` | `409` | Prepare and deploy the new signing secret before activating it. |
| `webhook_secret_rotation_overlap_active` | `409` | Retire the previous secret before preparing or activating another. |

For `freshness_ceiling_unsatisfied`, `error.freshness` includes `max_age_s` and `data_quality_status`. It also includes `actual_age_s` and `as_of` when the server has a clock to measure; no immediate retry time is promised.

## Wait or schedule a later request

| `error.reason` | HTTP | Action |
| - | - | - |
| `pick_not_released` | `404` | Schedule a request at `error.retry_at`. The advised time can change; do not poll repeatedly. |
| `read_model_warming` | `503` | This endpoint's prepared data is unavailable. Follow `Retry-After` for this endpoint. |
| `database_unavailable` | `503` | The database or its connection pool is unavailable. Follow `Retry-After`. |
| `request_accounting_unavailable` | `503` | Usage accounting had no capacity, so the handler did not run. Follow `Retry-After`. |
| `idempotency_in_progress` | `409` | A request with the same key is still running. Retry later with the same key and body. |
| `webhook_delivery_in_progress` | `409` | Wait for the active delivery before changing its URL or signing secret. |
| `monthly_quota_exceeded` | `429` | Enable pay as you go, obtain a higher ceiling if needed, or schedule the next request for the monthly reset. |
| `ip_rate_limited` | `429` | Follow `Retry-After` for the allowance shared by callers behind the same IP. |
| `ip_throttled` | `429` | Wait for the full IP cooldown. It can last minutes or days. |

A `503` without a reason means the rate limiter is unavailable. Back off authenticated calls for the specified delay.

<span id="retry-after" />

## Retry timing

These responses include both `Retry-After` in seconds and a future `error.retry_at`:

* Every `429` and `503`.
* `404` with reason `pick_not_released`.
* `408` on a `GET` or `HEAD`.

Use `Retry-After` for a delay because it avoids client clock differences. Use `retry_at` when scheduling a job for an absolute time; long quota and pick-release delays belong in a scheduler.

<Warning>
  A timed-out `POST`, `PATCH`, or `DELETE` may have completed on the server. It has no retry header. Check its state before repeating it, and reuse its idempotency key where the endpoint supports one.
</Warning>

<span id="handle-errors-in-code" />

## Example: bounded retries for a read

This Python example makes at most 3 attempts. It schedules neither pick release nor monthly reset, and leaves delays over 60 seconds to the caller:

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

HEADERS = {"Authorization": f"Bearer {os.environ['OXINSIDER_API_KEY']}"}

def get_json(path, **params):
    url = f"https://api.0xinsider.com/api/v1{path}"
    for attempt in range(3):
        response = requests.get(url, headers=HEADERS, params=params, timeout=40)
        body = response.json()
        if response.ok:
            return body

        error = body["error"]
        header = response.headers.get("Retry-After")
        seconds = int(header) if header is not None else None
        scheduled = error.get("reason") in ("pick_not_released", "monthly_quota_exceeded")
        if (attempt == 2 or scheduled or seconds is None
                or not 0 < seconds <= 60):
            raise RuntimeError(
                f"{error['code']}: {error['message']} "
                f"(request {body['meta']['request_id']})"
            )
        time.sleep(seconds)
```

Use a separate flow for writes so a timeout does not cause a duplicate action.

<span id="batch-items" />

## Check batch items too

A batch can return HTTP `200` while individual items fail. Inspect every `data[i].status`; an item with `status: error` has its own `error.code` and `error.message`.

<span id="what-an-error-does-not-tell-you" />

## Limits

HTTP success proves the batch request was handled, rather than proving every lookup succeeded.


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