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

# Errors

> Every Stophy API error code, its HTTP status, what it means, and whether to retry, with a retry loop you can copy for Node.js and Python.

Every error uses the same JSON shape. Look up `error.code` below.

```json theme={null}
{
  "success": false,
  "error": {
    "code": "rateLimited",
    "message": "Too many requests. Retry after the number of seconds in Retry-After.",
    "retryable": true,
    "retryAfterSeconds": 12,
    "requestId": "..."
  }
}
```

| Field | What it is |
| - | - |
| `code` | A stable code from the table below |
| `message` | What went wrong, in plain words |
| `retryable` | `true` when the same request may work later |
| `retryAfterSeconds` | Present when you should wait a set time. The `Retry-After` header has the same value. |
| `requestId` | The ID of this request. Include it when you contact support. |

Failed calls cost nothing.

## Error codes

| Code | Status | Retryable | What it means |
| - | - | - | - |
| `invalidRequest` | 400 | No | The input is not valid, or the source rejected a value you sent. The message names the problem. |
| `unauthorized` | 401 | No | The API key is missing or not valid. |
| `insufficientCredits` | 402 | No | Your balance is too low for this call. [Top up](/billing). |
| `forbidden` | 403 | No | This request is not allowed. |
| `notFound` | 404 | No | The endpoint, or the thing you asked for, does not exist. |
| `unsupportedMediaType` | 415 | No | Send the body as JSON with `content-type: application/json`. |
| `rateLimited` | 429 | Yes | Too many requests. Wait for `Retry-After`. |
| `tooManyInFlight` | 429 | Yes | Too many of your calls are running at once. Wait for one to finish. |
| `internalError` | 500, 503 | Yes | Something went wrong on our side. Try again shortly. |
| `sourceChanged` | 502 | No | The source answered in a shape we could not read. Retrying will not help yet. |
| `sourceUnavailable` | 503 | Yes | The source is not answering right now. Try again in a few seconds. |
| `sourceRefused` | 503 | Yes | The source is refusing requests right now. Try again in a few minutes. |
| `sourceTimeout` | 504 | Yes | The source took too long to answer. Try again. |

### Sign-in codes

These come only from the account sign-in flow, never from a data endpoint:

| Code | Status | Retryable | What it means |
| - | - | - | - |
| `invalidCliSession` | 400 | No | The sign-in session is not valid. |
| `invalidCliVerifier` | 400 | No | The sign-in verifier is not valid. |
| `cliSessionNotFound` | 404 | No | The sign-in session was not found or has expired. |
| `cliSessionExists` | 409 | No | The sign-in session already exists. |
| `cliSessionCompleted` | 409 | No | The sign-in session is no longer pending. |

## Retrying

* Retry only when `retryable` is `true`.
* If `retryAfterSeconds` or `Retry-After` is present, wait that long first. Otherwise back off: for example 0.5 s, then 1 s, then 2 s.
* Stop after a few tries.

<CodeGroup>
  ```ts TypeScript icon="https://mintcdn.com/sirathic/7E4w9-IC4ysabjfc/images/icons/typescript.svg?fit=max&auto=format&n=7E4w9-IC4ysabjfc&q=85&s=d8272a85e64145620f1fbbfcf7932986" theme={null}
  async function call(path: string, input: unknown, attempts = 3) {
    for (let attempt = 0; ; attempt++) {
      const response = await fetch(`https://api.stophy.dev${path}`, {
        method: "POST",
        headers: {
          authorization: `Bearer ${process.env.STOPHY_API_KEY}`,
          "content-type": "application/json",
        },
        body: JSON.stringify(input),
      });
      const body = await response.json();
      if (body.success) return body.data;
      if (!body.error.retryable || attempt + 1 >= attempts) throw new Error(body.error.message);
      const seconds = body.error.retryAfterSeconds ?? 0.5 * 2 ** attempt;
      await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
    }
  }
  ```

  ```python Python icon="python" theme={null}
  import os
  import time

  import requests


  def call(path, payload, attempts=3):
      for attempt in range(attempts):
          response = requests.post(
              f"https://api.stophy.dev{path}",
              headers={"Authorization": f"Bearer {os.environ['STOPHY_API_KEY']}"},
              json=payload,
              timeout=30,
          )
          body = response.json()
          if body["success"]:
              return body["data"]
          error = body["error"]
          if not error["retryable"] or attempt + 1 >= attempts:
              raise RuntimeError(error["message"])
          time.sleep(error.get("retryAfterSeconds", 0.5 * 2**attempt))
  ```
</CodeGroup>
