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

# Error handling

> Error response format, status codes, and retry strategies for the Andi AI Search API.

When a request fails, the API returns an error response with an HTTP status code and a JSON body describing the problem.

## Error response format

All errors return both an `error` field and a `message` field:

```json theme={null}
{
  "error": "Unauthorized",
  "message": "Invalid API key"
}
```

## Status codes

| Code  | Error                   | Description                                                         |
| ----- | ----------------------- | ------------------------------------------------------------------- |
| `400` | Varies                  | Missing or invalid parameters. Check the `error` field for details. |
| `401` | `Unauthorized`          | Missing or invalid API key.                                         |
| `402` | `Insufficient Credits`  | Account balance depleted.                                           |
| `429` | `Too Many Requests`     | Rate limit exceeded.                                                |
| `500` | `Internal server error` | Something went wrong on our end.                                    |

<AccordionGroup>
  <Accordion title="400 — Bad request">
    **Causes:** Missing `q` parameter, invalid parameter values, malformed JSON array in `q`.

    **Example messages:**

    * `Missing required parameter: q`
    * `Invalid value for depth parameter`

    **Fix:** Check your query string parameters against the [parameter reference](/features/query-parameters).
  </Accordion>

  <Accordion title="401 — Unauthorized">
    **Causes:** Missing `x-api-key` header, invalid or revoked API key.

    **Example messages:**

    * `Missing x-api-key header`
    * `Invalid API key`

    **Fix:** Verify your API key in the [API Console](https://console.andiai.com). Make sure you're passing it in the `x-api-key` header (not as a query parameter or in the `Authorization` header).
  </Accordion>

  <Accordion title="402 — Insufficient credits">
    **Cause:** Your account has no remaining credits.

    **Message:** `Your account has insufficient credits. Please add credits to continue.`

    **Fix:** [Add credits](https://console.andiai.com) to your account.
  </Accordion>

  <Accordion title="429 — Rate limit exceeded">
    **Cause:** Too many requests in the current time window.

    **Message:** `Rate limit of N requests per second exceeded`

    **Fix:** Back off and retry after the period specified in the `Retry-After` header. See [rate limits](/resources/rate-limits) for monitoring usage.
  </Accordion>

  <Accordion title="500 — Internal server error">
    **Cause:** An unexpected error on our side.

    **Fix:** Retry with exponential backoff. If it persists, contact [support](mailto:hello+api@andiai.com).
  </Accordion>
</AccordionGroup>

## Handling errors in code

Check the HTTP status code before parsing the response body:

<CodeGroup>
  ```bash curl theme={null}
  response=$(curl -s -w "\n%{http_code}" \
    "https://search-api.andisearch.com/api/v1/search?q=test" \
    -H "x-api-key: $ANDI_API_KEY")

  http_code=$(echo "$response" | tail -1)
  body=$(echo "$response" | sed '$d')

  case $http_code in
    200) echo "$body" | jq '.results[:3]' ;;
    429) echo "Rate limited. Retry after $(echo "$body" | jq -r '.message')" ;;
    402) echo "Out of credits. Visit console.andiai.com" ;;
    *)   echo "Error $http_code: $(echo "$body" | jq -r '.error')" ;;
  esac
  ```

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

  response = requests.get(
      "https://search-api.andisearch.com/api/v1/search",
      params={"q": "test"},
      headers={"x-api-key": "YOUR_API_KEY"},
  )

  if response.status_code == 200:
      data = response.json()
      print(f"Found {len(data['results'])} results")
  elif response.status_code == 429:
      retry_after = response.headers.get("Retry-After", 1)
      print(f"Rate limited. Retry after {retry_after} seconds.")
  elif response.status_code == 402:
      print("Out of credits. Add credits at console.andiai.com")
  else:
      error = response.json()
      print(f"Error {response.status_code}: {error['error']}")
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://search-api.andisearch.com/api/v1/search?q=test",
    { headers: { "x-api-key": "YOUR_API_KEY" } }
  );

  if (response.ok) {
    const data = await response.json();
    console.log(`Found ${data.results.length} results`);
  } else if (response.status === 429) {
    const retryAfter = response.headers.get("Retry-After") || 1;
    console.log(`Rate limited. Retry after ${retryAfter}s`);
  } else if (response.status === 402) {
    console.log("Out of credits. Add credits at console.andiai.com");
  } else {
    const error = await response.json();
    console.log(`Error ${response.status}: ${error.error}`);
  }
  ```
</CodeGroup>

## Retry with exponential backoff

For transient errors (`429`, `500`), retry with increasing delays:

<CodeGroup>
  ```python Python theme={null}
  import time
  import requests


  def search_with_retry(query: str, max_retries: int = 5) -> dict:
      """Search with exponential backoff on transient errors."""
      delay = 1

      for attempt in range(max_retries + 1):
          response = requests.get(
              "https://search-api.andisearch.com/api/v1/search",
              params={"q": query},
              headers={"x-api-key": "YOUR_API_KEY"},
          )

          if response.status_code == 200:
              return response.json()

          if response.status_code in (429, 500) and attempt < max_retries:
              wait = int(response.headers.get("Retry-After", delay))
              time.sleep(wait)
              delay = min(delay * 2, 60)
              continue

          response.raise_for_status()
  ```

  ```javascript JavaScript theme={null}
  async function searchWithRetry(query, maxRetries = 5) {
    let delay = 1000;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      const response = await fetch(
        `https://search-api.andisearch.com/api/v1/search?q=${encodeURIComponent(query)}`,
        { headers: { "x-api-key": "YOUR_API_KEY" } }
      );

      if (response.ok) return response.json();

      if ((response.status === 429 || response.status === 500) && attempt < maxRetries) {
        const retryAfter = response.headers.get("Retry-After");
        const wait = retryAfter ? parseInt(retryAfter) * 1000 : delay;
        await new Promise((r) => setTimeout(r, wait));
        delay = Math.min(delay * 2, 60000);
        continue;
      }

      throw new Error(`Search failed: ${response.status}`);
    }
  }
  ```
</CodeGroup>

<Warning>
  Do not retry `400`, `401`, or `402` errors — these indicate problems that won't resolve by retrying. Fix the request or account issue first.
</Warning>

## Troubleshooting

| Symptom                         | Likely cause                          | Fix                                                            |
| ------------------------------- | ------------------------------------- | -------------------------------------------------------------- |
| `Missing x-api-key header`      | Header not sent                       | Add `-H "x-api-key: YOUR_KEY"` to your request                 |
| `Invalid API key`               | Wrong or revoked key                  | Check the key in the [API Console](https://console.andiai.com) |
| `Missing required parameter: q` | No query provided                     | Add `?q=your+query` to the URL                                 |
| Slow responses                  | Using `depth=deep` or `metadata=full` | Switch to `depth=fast` or `metadata=basic` if speed matters    |
| Empty `results` array           | No matches for query/filters          | Broaden your query or loosen filters                           |

## Next steps

<CardGroup cols={2}>
  <Card title="Rate limits" icon="gauge" href="/resources/rate-limits">
    Rate limit headers and monitoring.
  </Card>

  <Card title="Authentication" icon="key" href="/getting-started/authentication">
    API key management and security.
  </Card>
</CardGroup>
