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

# Errors & Refunds

> Every error format, status code, and error type the LLMLayer API returns - plus when you are automatically refunded.

## Error Formats

LLMLayer returns errors in two shapes, depending on where the request fails.

### 1. Structured Errors (most endpoint errors)

Endpoint-level failures return a structured envelope inside FastAPI's `detail` field:

```json theme={null}
{
  "detail": {
    "error_type": "validation_error",
    "error_code": "missing_json_schema",
    "message": "json_schema is required when 'json' is in modes. ...",
    "details": { "url": "https://www.ycombinator.com" }
  }
}
```

| Field        | Description                                          |
| ------------ | ---------------------------------------------------- |
| `error_type` | Error category — see the [table below](#error-types) |
| `error_code` | Machine-readable code, stable per failure case       |
| `message`    | Human-readable explanation                           |
| `details`    | Optional context (URL, provider, status code, ...)   |

### 2. Plain-String Errors (account-level guards)

Account-level checks (API key, balance, rate limit) return a plain string in `detail`:

```json theme={null}
{ "detail": "insufficient funds on LLMLAYER, please top up your account" }
```

These occur **before** your request is processed, so they never incur any charge.

***

## HTTP Status Codes

| Status | Meaning                                                                           | Charged?                                    | Retry?                      |
| ------ | --------------------------------------------------------------------------------- | ------------------------------------------- | --------------------------- |
| `400`  | Invalid request — missing or malformed parameter                                  | No                                          | After fixing the request    |
| `401`  | Missing or invalid API key                                                        | No                                          | After fixing the key        |
| `402`  | Insufficient credits                                                              | No                                          | After topping up            |
| `403`  | Blocked target (private IP, blocked domain or port)                               | No                                          | No — target not allowed     |
| `422`  | Unprocessable — e.g. empty page content (refunded) or extraction output truncated | Depends — see [Refunds](#automatic-refunds) | After adjusting the request |
| `423`  | Billing reconciliation in progress for this key                                   | No                                          | Yes, after a short wait     |
| `429`  | Rate limit exceeded for your [tier](/rate-limits)                                 | No                                          | Yes, with backoff           |
| `500`  | Internal error or upstream fetch failure                                          | Depends — see [Refunds](#automatic-refunds) | Yes                         |
| `502`  | Upstream service failure (e.g. brand profile, model output invalid)               | Depends — see [Refunds](#automatic-refunds) | Yes                         |
| `504`  | Operation timeout (e.g. PDF scrape, DNS resolution)                               | —                                           | Yes                         |

***

## Error Types

The `error_type` field groups errors into categories:

| `error_type`           | Used for                                                       | Typical statuses                 |
| ---------------------- | -------------------------------------------------------------- | -------------------------------- |
| `validation_error`     | Bad or missing request parameters                              | 400                              |
| `authentication_error` | Missing API key header                                         | 401                              |
| `provider_error`       | An upstream AI provider failed (rate limit, outage, auth)      | 401, 429, 500                    |
| `scraping_error`       | Scrape/PDF/URL-validation failures (Scraper, PDF, URL checks)  | 400, 403, 500, 504               |
| `search_error`         | Web search failures (Answer API)                               | 500                              |
| `extraction_error`     | Extract API failures (fetch, empty content, truncation, brand) | 422, 500, 502                    |
| `map_error`            | Map endpoint failures                                          | 4xx/5xx (passes upstream status) |
| `internal_error`       | Unexpected server errors                                       | 500                              |

<Info>
  Streaming endpoints (`/answer_stream`, `/crawl_stream`) deliver mid-stream failures as SSE frames of the form `{"type": "error", "error": "..."}` instead of HTTP error responses, because the HTTP status is already committed once streaming starts.
</Info>

***

## Automatic Refunds

<Card title="You only pay when the work happens" icon="rotate-left">
  The **Extract API** debits your selected modes up front, then **automatically refunds the full amount** if the request fails before any AI cost is incurred:

  | Failure                              | Error code           | Status                       | Refunded      |
  | ------------------------------------ | -------------------- | ---------------------------- | ------------- |
  | Target page could not be fetched     | `page_fetch_failed`  | 500 (or the target's status) | ✅ Full refund |
  | Page returned no extractable content | `empty_page_content` | 422                          | ✅ Full refund |
  | Brand profile could not be built     | `brand_fetch_failed` | 502                          | ✅ Full refund |

  Refunded errors say so explicitly: *"You have not been charged for this request."*
</Card>

<Note>
  Failures that happen **after** AI processing has started (e.g. an upstream model outage, or `output_truncated` when extracted JSON exceeds the size limit) are not refunded, because the model cost was already incurred.
</Note>

Account-level rejections (401, 402, 423, 429) always happen **before** any charge.

***

## SDK Exception Mapping

Both official SDKs raise typed exceptions so you can branch cleanly:

| Condition                                                                        | Python (`llmlayer.exceptions`) | TypeScript (`llmlayer`)      |
| -------------------------------------------------------------------------------- | ------------------------------ | ---------------------------- |
| `validation_error`, or status 400 / 422                                          | `InvalidRequest`               | `InvalidRequest`             |
| `authentication_error`, or status 401 / 403                                      | `AuthenticationError`          | `AuthenticationError`        |
| `provider_error`                                                                 | `ProviderError`                | `ProviderError`              |
| Status 429                                                                       | `RateLimitError`               | `RateLimitError`             |
| `internal_error`, `scraping_error`, `search_error`, `map_error`, or status ≥ 500 | `InternalServerError`          | `InternalServerError`        |
| Anything else (e.g. 402 insufficient credits, 423)                               | `LLMLayerError` (base class)   | `LLMLayerError` (base class) |

All exceptions inherit from `LLMLayerError`, so `except LLMLayerError` / `catch` on the base class catches everything.

### Recommended Retry Strategy

<CodeGroup>
  ```typescript TypeScript theme={null}
  import {
    LLMLayerClient,
    InvalidRequest,
    AuthenticationError,
    RateLimitError,
    InternalServerError,
  } from 'llmlayer';

  async function withRetries<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
    for (let attempt = 0; ; attempt++) {
      try {
        return await fn();
      } catch (error) {
        // Never retry: fix the request or the key instead
        if (error instanceof InvalidRequest) throw error;
        if (error instanceof AuthenticationError) throw error;

        // Retry with backoff: rate limits and server-side failures
        const retryable = error instanceof RateLimitError || error instanceof InternalServerError;
        if (!retryable || attempt >= maxRetries - 1) throw error;
        await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
      }
    }
  }
  ```

  ```python Python theme={null}
  import time
  from llmlayer.exceptions import (
      InvalidRequest,
      AuthenticationError,
      RateLimitError,
      InternalServerError,
  )

  def with_retries(fn, max_retries=3):
      for attempt in range(max_retries):
          try:
              return fn()
          except (InvalidRequest, AuthenticationError):
              raise  # never retry: fix the request or the key instead
          except (RateLimitError, InternalServerError):
              if attempt == max_retries - 1:
                  raise
              time.sleep(2 ** attempt)  # backoff, then retry
  ```
</CodeGroup>

***

## Common Error Codes by Endpoint

<AccordionGroup>
  <Accordion title="All endpoints">
    | Code                                                 | Status | Meaning                                                    |
    | ---------------------------------------------------- | ------ | ---------------------------------------------------------- |
    | `missing_llmlayer_api_key`                           | 401    | No `Authorization: Bearer <key>` header                    |
    | *(plain string)* `LLMLAYER API KEY NOT VALID`        | 401    | Key is malformed, revoked, or unknown                      |
    | *(plain string)* `insufficient funds on LLMLAYER...` | 402    | Credit balance too low — [top up](https://app.llmlayer.ai) |
    | *(plain string)* `rate N/min exceeded on LLMLAYER`   | 429    | Over your tier's requests-per-minute                       |
  </Accordion>

  <Accordion title="Extract API">
    | Code                                                                            | Status              | Refunded                                        |
    | ------------------------------------------------------------------------------- | ------------------- | ----------------------------------------------- |
    | `missing_modes` / `missing_json_schema` / `missing_query` / `pdf_not_supported` | 400                 | Nothing charged                                 |
    | `page_fetch_failed`                                                             | 500 / target status | ✅ Yes                                           |
    | `empty_page_content`                                                            | 422                 | ✅ Yes                                           |
    | `brand_fetch_failed`                                                            | 502                 | ✅ Yes                                           |
    | `output_truncated`                                                              | 422                 | No (AI ran) — reduce schema fields or page size |
    | `model_json_invalid`                                                            | 502                 | No (AI ran) — safe to retry                     |
  </Accordion>

  <Accordion title="Scraper / PDF / Crawl / Map">
    | Code                                                        | Status | Meaning                                                     |
    | ----------------------------------------------------------- | ------ | ----------------------------------------------------------- |
    | `invalid_url` / `unsupported_scheme` / `credentials_in_url` | 400    | URL malformed or not http(s)                                |
    | `blocked_domain` / `blocked_ip` / `blocked_port`            | 403    | Target not allowed (security policy)                        |
    | `dns_resolution_failed`                                     | 400    | Host did not resolve                                        |
    | `url_scrape_failed` / `pdf_scrape_failed`                   | 500    | Upstream fetch failed — retry or use `advanced_proxy`       |
    | `pdf_scrape_timeout`                                        | 504    | PDF took too long                                           |
    | `pdf_not_supported`                                         | 400    | PDF URL sent to a non-PDF endpoint — use `/get_pdf_content` |
  </Accordion>

  <Accordion title="Answer API">
    | Code                                                                   | Status          | Meaning                                   |
    | ---------------------------------------------------------------------- | --------------- | ----------------------------------------- |
    | `missing_query` / `missing_model` / `missing_json_schema`              | 400             | Required parameter absent                 |
    | `invalid_search_parameters`                                            | 400             | Bad search\_type/date\_filter combination |
    | `query_generation_error` / `search_context_error`                      | 500             | Search pipeline failure — retry           |
    | `<provider>_rate_limit` / `<provider>_auth_error` / `<provider>_error` | 429 / 401 / 500 | Upstream AI provider issue                |
  </Accordion>
</AccordionGroup>

***

## Need Help?

<CardGroup cols={2}>
  <Card title="Discord Community" icon="discord" href="https://discord.gg/EqQF4cjTq5">
    Chat with other developers
  </Card>

  <Card title="Email Support" icon="envelope" href="mailto:support@llmlayer.ai">
    [support@llmlayer.ai](mailto:support@llmlayer.ai)
  </Card>
</CardGroup>
