<!-- https://pricto.dev/docs/errores · Spanish version: https://pricto.dev/docs/errores.md -->

# Errors

Errors follow RFC 9457 (`application/problem+json`). The `code` field is stable: code against it, not against `title` or `detail`. `hint` says what to do next and is written so an agent can act on its own. Errors are never charged.

| Field | What it is |
|---|---|
| `code` | Stable code, in the table below |
| `status` | HTTP status |
| `title` | Title in the requested language |
| `detail` | What happened, in the requested language |
| `hint` | What to do |
| `param` | The offending parameter, if any |
| `request_id` | To ask for help; also in the `X-Request-Id` header |
| `docs_url` | Link to this page |

## Catalog

| Code | HTTP | When |
|---|---|---|
| [`invalid_parameter`](#invalid_parameter) | 400 | A parameter is missing, malformed or unknown |
| [`unauthorized`](#unauthorized) | 401 | No key was sent, or the key doesn't exist or was revoked |
| [`insufficient_credits`](#insufficient_credits) | 402 | The balance doesn't cover even one item |
| [`spend_cap_reached`](#spend_cap_reached) | 402 | You reached your account's monthly spend cap |
| [`plan_required`](#plan_required) | 403 | Your plan doesn't include what you asked for, for example a history range entirely outside its window |
| [`not_found`](#not_found) | 404 | The product, offer, store or resource doesn't exist |
| [`rate_limited`](#rate_limited) | 429 | You went over your key's per-second, per-minute or per-day limit |
| [`not_implemented`](#not_implemented) | 501 | The endpoint is planned and doesn't exist yet |
| [`coverage_unavailable`](#coverage_unavailable) | 404 | Reserved for a country, store or category we don't cover |
| [`internal_error`](#internal_error) | 500 | Our error |
| [`service_unavailable`](#service_unavailable) | 503 | The service is briefly unavailable |

The examples are real API responses to a request that triggers each error, except `coverage_unavailable`, `internal_error` and `service_unavailable`, which show the body shape.

### `invalid_parameter`

| HTTP | Retry |
|---|---|
| 400 | No |

**Cause:** A parameter is missing, malformed or unknown.

**What to do:** Fix the parameter named in `param`; `hint` lists the valid values.

```json
{
  "type": "https://pricto.dev/docs#invalid_parameter",
  "title": "Parámetro inválido",
  "status": 400,
  "code": "invalid_parameter",
  "detail": "'limit' debe ser un entero entre 1 y 20.",
  "param": "limit",
  "request_id": "req_b8bd2c0560154bb6a949",
  "docs_url": "https://pricto.dev/docs#invalid_parameter"
}
```

### `unauthorized`

| HTTP | Retry |
|---|---|
| 401 | No |

**Cause:** No key was sent, or the key doesn't exist or was revoked.

**What to do:** Send `Authorization: Bearer <key>`. To try it, use `sk_test_sandbox`.

```json
{
  "type": "https://pricto.dev/docs#unauthorized",
  "title": "API key ausente o inválida",
  "status": 401,
  "code": "unauthorized",
  "detail": "Falta la API key.",
  "hint": "Envía el header 'Authorization: Bearer <api_key>'. Para probar con datos de ejemplo usa la key pública sk_test_sandbox.",
  "request_id": "req_421eba4eb1a7494caa28",
  "docs_url": "https://pricto.dev/docs#unauthorized"
}
```

### `insufficient_credits`

| HTTP | Retry |
|---|---|
| 402 | No |

**Cause:** The balance doesn't cover even one item. Nothing was charged.

**What to do:** Buy credits or upgrade; the body carries `balance`, `usd_needed` and the `pricing` block.

```json
{
  "type": "https://pricto.dev/docs#insufficient_credits",
  "title": "Créditos insuficientes",
  "status": 402,
  "code": "insufficient_credits",
  "detail": "Se acabaron los 100 créditos gratis de este mes (se renuevan el 1 de noviembre). Puedes comprar créditos desde USD 10, que duran 12 meses, o pasar a Builder: USD 29 al mes por 15.000 créditos.",
  "hint": "No se cobró esta consulta. Repítele este mensaje a tu humano tal cual.",
  "request_id": "req_9506056d01054faea040",
  "docs_url": "https://pricto.dev/docs#insufficient_credits",
  "balance": 0,
  "credits_needed": 1,
  "usd_needed": 0.01,
  "top_up_url": "https://pricto.dev/#precios",
  "pricing": {
    "currency": "USD",
    "usd_per_1000_credits": 3,
    "min_top_up_usd": 10,
    "credits_per_min_top_up": 3333,
    "prepaid_validity_months": 12,
    "plans": [
      {
        "id": "builder",
        "usd_per_month": 29,
        "credits_per_month": 15000
      },
      {
        "id": "pro",
        "usd_per_month": 99,
        "credits_per_month": 75000
      },
      {
        "id": "business",
        "usd_per_month": 299,
        "credits_per_month": 300000
      },
      {
        "id": "scale",
        "usd_per_month": 999,
        "credits_per_month": 1500000
      }
    ],
    "top_up_url": "https://pricto.dev/#precios"
  }
}
```

### `spend_cap_reached`

| HTTP | Retry |
|---|---|
| 402 | No |

**Cause:** You reached your account's monthly spend cap.

**What to do:** Raise the cap in the console or wait for the next cycle (`period_resets_at`).

```json
{
  "type": "https://pricto.dev/docs#spend_cap_reached",
  "title": "Tope de gasto mensual alcanzado",
  "status": 402,
  "code": "spend_cap_reached",
  "detail": "Llegaste al tope de gasto mensual (0 créditos).",
  "hint": "No se cobró esta consulta. Sube el tope en la consola o espera el próximo ciclo.",
  "request_id": "req_ddb8e86585aa40e986f6",
  "docs_url": "https://pricto.dev/docs#spend_cap_reached"
}
```

### `plan_required`

| HTTP | Retry |
|---|---|
| 403 | No |

**Cause:** Your plan doesn't include what you asked for, for example a history range entirely outside its window.

**What to do:** Ask for a range within your plan's window or upgrade.

```json
{
  "type": "https://pricto.dev/docs#plan_required",
  "title": "Requiere otro plan",
  "status": 403,
  "code": "plan_required",
  "detail": "Tu plan incluye 30 días de historial (desde 2026-09-24) y todo el rango pedido es anterior.",
  "hint": "Pide fechas desde 2026-09-24 o sube de plan.",
  "param": "from",
  "request_id": "req_521c65ce7a06430586b8",
  "docs_url": "https://pricto.dev/docs#plan_required"
}
```

### `not_found`

| HTTP | Retry |
|---|---|
| 404 | No |

**Cause:** The product, offer, store or resource doesn't exist.

**What to do:** Check the id; search with `/v1/products/search` to get a valid one.

```json
{
  "type": "https://pricto.dev/docs#not_found",
  "title": "No encontrado",
  "status": 404,
  "code": "not_found",
  "detail": "No existe el producto 'prd_noexiste'.",
  "hint": "Busca primero con /products/search (o la tool search_products) y usa el 'id' que devuelve.",
  "param": "product_id",
  "request_id": "req_ccff584bbffc4a5e80a9",
  "docs_url": "https://pricto.dev/docs#not_found"
}
```

### `rate_limited`

| HTTP | Retry |
|---|---|
| 429 | Yes, with backoff |

**Cause:** You went over your key's per-second, per-minute or per-day limit.

**What to do:** Wait the seconds in `Retry-After` and retry with backoff.

```json
{
  "type": "https://pricto.dev/docs#rate_limited",
  "title": "Demasiadas solicitudes",
  "status": 429,
  "code": "rate_limited",
  "detail": "Límite de 1 solicitudes por segundo.",
  "hint": "Reintenta en 1 s o agrupa con /prices/batch.",
  "request_id": "req_8bc5e9f8776c4e45a9e6",
  "docs_url": "https://pricto.dev/docs#rate_limited"
}
```

### `not_implemented`

| HTTP | Retry |
|---|---|
| 501 | No |

**Cause:** The endpoint is planned and doesn't exist yet.

**What to do:** Use the alternative given in `hint`.

```json
{
  "type": "https://pricto.dev/docs#not_implemented",
  "title": "Todavía no disponible",
  "status": 501,
  "code": "not_implemented",
  "detail": "Exports está planificado y todavía no está disponible.",
  "hint": "Para volúmenes grandes usa /offers con format=csv y paginación.",
  "request_id": "req_58d4118acb0a48d4ba7d",
  "docs_url": "https://pricto.dev/docs#not_implemented"
}
```

### `coverage_unavailable`

| HTTP | Retry |
|---|---|
| 404 | No |

**Cause:** Reserved for a country, store or category we don't cover. The API doesn't emit it yet: today it answers 200 with an empty list and a notice in `meta.warnings`.

**What to do:** Check `/v1/coverage` to see what exists, or request coverage with `POST /v1/data-requests`.

```json
{
  "type": "https://pricto.dev/docs#coverage_unavailable",
  "title": "Sin cobertura",
  "status": 404,
  "code": "coverage_unavailable",
  "detail": "No cubrimos el país 'PE' todavía.",
  "hint": "Revisa /v1/coverage o pide la cobertura con POST /v1/data-requests.",
  "param": "country",
  "request_id": "req_ejemplo",
  "docs_url": "https://pricto.dev/docs#coverage_unavailable"
}
```

### `internal_error`

| HTTP | Retry |
|---|---|
| 500 | Yes, with backoff |

**Cause:** Our error. Nothing was charged.

**What to do:** Retry with backoff; if it persists, send the `request_id`.

```json
{
  "type": "https://pricto.dev/docs#internal_error",
  "title": "Error interno",
  "status": 500,
  "code": "internal_error",
  "detail": "Error inesperado.",
  "request_id": "req_ejemplo",
  "docs_url": "https://pricto.dev/docs#internal_error"
}
```

### `service_unavailable`

| HTTP | Retry |
|---|---|
| 503 | Yes, with backoff |

**Cause:** The service is briefly unavailable.

**What to do:** Retry after `Retry-After`.

```json
{
  "type": "https://pricto.dev/docs#service_unavailable",
  "title": "Servicio no disponible",
  "status": 503,
  "code": "service_unavailable",
  "detail": "El servicio no está disponible por un momento.",
  "request_id": "req_ejemplo",
  "docs_url": "https://pricto.dev/docs#service_unavailable"
}
```
