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

# Errors

> A single structured error shape, standard HTTP statuses, and stable codes you can branch on.

Every error returns the same JSON shape with a standard HTTP status. There's one thing to parse, whatever went wrong.

```json theme={null}
{
  "statusCode": 400,
  "code": "VALIDATION_ERROR",
  "message": "url must be a valid http(s) URL."
}
```

<ResponseField name="statusCode" type="number">
  The HTTP status, mirrored in the body for convenience.
</ResponseField>

<ResponseField name="code" type="string">
  A stable, machine-readable code. Branch on this — the wording of `message` may change; the code won't.
</ResponseField>

<ResponseField name="message" type="string">
  A human-readable explanation of what went wrong.
</ResponseField>

<Note>
  Failed calls are free. Because credits are charged only on success, an error never costs you anything — see [Credits](/concepts/credits).
</Note>

## Statuses

<AccordionGroup>
  <Accordion title="400 — Validation error" icon="https://mintcdn.com/hydrafetch/DnAi7n_kype0jB2E/icons/circle-warning.svg?fit=max&auto=format&n=DnAi7n_kype0jB2E&q=85&s=60e00931cb258437df1d089ad4804d45" width="18" height="18" data-path="icons/circle-warning.svg">
    The request body didn't pass validation — a missing `url`, a non-http scheme, an out-of-range `maxAge`, or requesting the `json` format without `jsonOptions`. Fix the request and retry.

    ```json theme={null}
    {
      "statusCode": 400,
      "code": "VALIDATION_ERROR",
      "message": "jsonOptions.schema or jsonOptions.prompt is required when requesting the json format."
    }
    ```
  </Accordion>

  <Accordion title="401 — Unauthorized" icon="https://mintcdn.com/hydrafetch/koPVMLXM3S4OTC3p/icons/key.svg?fit=max&auto=format&n=koPVMLXM3S4OTC3p&q=85&s=b93ecee0af6f88190e37943e72c84735" width="18" height="18" data-path="icons/key.svg">
    The `X-API-Key` header is missing, malformed, or revoked. The check runs before any work, so a rejected request never costs credits. See [Authentication](/authentication).

    ```json theme={null}
    {
      "statusCode": 401,
      "code": "UNAUTHORIZED",
      "message": "Invalid API key."
    }
    ```
  </Accordion>

  <Accordion title="402 — Insufficient credits" icon="https://mintcdn.com/hydrafetch/koPVMLXM3S4OTC3p/icons/coins.svg?fit=max&auto=format&n=koPVMLXM3S4OTC3p&q=85&s=9356ce31843856848fdf477d18dba722" width="18" height="18" data-path="icons/coins.svg">
    Your workspace doesn't have enough credits to cover the call. It's rejected up front, before any work runs, so nothing is charged. Top up and retry.

    ```json theme={null}
    {
      "statusCode": 402,
      "code": "INSUFFICIENT_CREDITS",
      "message": "Not enough credits to complete this request."
    }
    ```
  </Accordion>

  <Accordion title="404 — Not found" icon="https://mintcdn.com/hydrafetch/DnAi7n_kype0jB2E/icons/magnifier.svg?fit=max&auto=format&n=DnAi7n_kype0jB2E&q=85&s=647287daa598be03f482f23872531f5b" width="18" height="18" data-path="icons/magnifier.svg">
    The resource doesn't exist — an unknown job, crawl, or batch id — or a `cacheOnly` scrape found no fresh capture to serve. A `cacheOnly` miss is expected and never triggers a live fetch; see [Caching](/concepts/caching).

    ```json theme={null}
    {
      "statusCode": 404,
      "code": "NOT_FOUND",
      "message": "No cached capture available for this URL."
    }
    ```
  </Accordion>

  <Accordion title="422 — Origin unreachable" icon="https://mintcdn.com/hydrafetch/DnAi7n_kype0jB2E/icons/link-broken.svg?fit=max&auto=format&n=DnAi7n_kype0jB2E&q=85&s=ee463a072edd4e56cf8d60c33e86aca0" width="18" height="18" data-path="icons/link-broken.svg">
    The target site did not respond, so there was nothing to return. This says something about that URL rather than about our service, and retrying it will not change the answer: treat it as a dead address and move on. Nothing was charged.

    ```json theme={null}
    {
      "statusCode": 422,
      "code": "UPSTREAM_UNREACHABLE",
      "message": "The origin did not respond to any attempt we made."
    }
    ```
  </Accordion>

  <Accordion title="429 — Rate limited" icon="https://mintcdn.com/hydrafetch/koPVMLXM3S4OTC3p/icons/gauge.svg?fit=max&auto=format&n=koPVMLXM3S4OTC3p&q=85&s=70970b1e5a9495b267194e7bbc11d0d7" width="18" height="18" data-path="icons/gauge.svg">
    You've sent requests faster than your allowance. Back off and retry after a short delay, ideally with exponential backoff.

    ```json theme={null}
    {
      "statusCode": 429,
      "code": "TOO_MANY_REQUESTS",
      "message": "Too many requests. Slow down and retry shortly."
    }
    ```
  </Accordion>

  <Accordion title="504 — Timed out" icon="https://mintcdn.com/hydrafetch/DnAi7n_kype0jB2E/icons/clock.svg?fit=max&auto=format&n=DnAi7n_kype0jB2E&q=85&s=ffd6e437f4f72e0890c4bfb8890bfe6d" width="18" height="18" data-path="icons/clock.svg">
    The page ran past the 90-second synchronous budget. The fetch keeps going in the background, so
    the capture usually lands in the cache and an immediate retry returns it quickly. Pages that hit
    this are almost always behind heavy bot protection. If you would rather not wait at all, send
    `async: true` and poll the job instead.

    ```json theme={null}
    {
      "statusCode": 504,
      "code": "REQUEST_TIMEOUT",
      "message": "Request exceeded its 90s budget and was cancelled.",
      "details": { "budgetMs": 90000, "path": "scrape", "retryable": true }
    }
    ```
  </Accordion>

  <Accordion title="5xx — Server error" icon="https://mintcdn.com/hydrafetch/DnAi7n_kype0jB2E/icons/server.svg?fit=max&auto=format&n=DnAi7n_kype0jB2E&q=85&s=3c94638775a3414a31615ccf5e9037bd" width="18" height="18" data-path="icons/server.svg">
    Something went wrong on our side, or the target page couldn't be delivered within the time budget. These are safe to retry — the failed call wasn't charged.

    ```json theme={null}
    {
      "statusCode": 500,
      "code": "INTERNAL_SERVER_ERROR",
      "message": "An unexpected error occurred. Please retry."
    }
    ```
  </Accordion>
</AccordionGroup>

## Handling errors

Read `code` to decide what to do, not the HTTP status alone:

* **`VALIDATION_ERROR`** — fix the request; retrying as-is won't help.
* **`UNAUTHORIZED`** — check the key.
* **`INSUFFICIENT_CREDITS`** — top up, then retry.
* **`NOT_FOUND`** — for `cacheOnly`, fall back to a live fetch if you want the page.
* **`REQUEST_TIMEOUT`** — retry once; the background fetch usually makes the second call fast. Use `async: true` if you would rather not wait.
* **`UPSTREAM_UNREACHABLE`** — the site is not answering; do not retry it.
* **`TOO_MANY_REQUESTS` and `5xx`** — retry with exponential backoff.

<Warning>
  Job-level failure is different from a request error. A crawl or batch returns `200` while individual pages may fail — check each page's `status` and `error` in the job's `pages` array, and the job's own `status` for `failed` or `cancelled`. See [Jobs & webhooks](/concepts/jobs-and-webhooks).
</Warning>


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