<!-- https://pricto.dev/docs/errores · versión en inglés: https://pricto.dev/docs/errores.en.md -->

# Errores

Los errores siguen RFC 9457 (`application/problem+json`). El campo `code` es estable: programa contra él, no contra `title` ni `detail`. `hint` dice qué hacer después y está escrito para que un agente pueda actuar solo. Un error nunca cobra.

| Campo | Qué es |
|---|---|
| `code` | Código estable, en la tabla de abajo |
| `status` | Código HTTP |
| `title` | Título en el idioma pedido |
| `detail` | Qué pasó, en el idioma pedido |
| `hint` | Qué hacer |
| `param` | El parámetro con problemas, si aplica |
| `request_id` | Para pedir ayuda; también en la cabecera `X-Request-Id` |
| `docs_url` | Enlace a esta página |

## Catálogo

| Código | HTTP | Cuándo |
|---|---|---|
| [`invalid_parameter`](#invalid_parameter) | 400 | Un parámetro falta, está mal formado o no existe |
| [`unauthorized`](#unauthorized) | 401 | No mandaste key, o la key no existe o fue revocada |
| [`insufficient_credits`](#insufficient_credits) | 402 | El saldo no alcanza ni para un ítem |
| [`spend_cap_reached`](#spend_cap_reached) | 402 | Llegaste al tope de gasto mensual de tu cuenta |
| [`plan_required`](#plan_required) | 403 | Tu plan no incluye lo que pediste, por ejemplo un rango de historial completo fuera de su ventana |
| [`not_found`](#not_found) | 404 | El producto, la oferta, la tienda o el recurso no existe |
| [`rate_limited`](#rate_limited) | 429 | Pasaste el límite de consultas por segundo, minuto o día de tu key |
| [`not_implemented`](#not_implemented) | 501 | El endpoint está planificado y todavía no existe |
| [`coverage_unavailable`](#coverage_unavailable) | 404 | Reservado para un país, tienda o categoría que no cubrimos |
| [`internal_error`](#internal_error) | 500 | Un error nuestro |
| [`service_unavailable`](#service_unavailable) | 503 | El servicio no está disponible por un momento |

Los ejemplos son respuestas reales de la API a un pedido que provoca cada error, salvo `coverage_unavailable`, `internal_error` y `service_unavailable`, que muestran la forma del cuerpo.

### `invalid_parameter`

| HTTP | Reintentar |
|---|---|
| 400 | No |

**Causa:** Un parámetro falta, está mal formado o no existe.

**Qué hacer:** Corrige el parámetro que indica `param`; `hint` trae los valores válidos.

```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 | Reintentar |
|---|---|
| 401 | No |

**Causa:** No mandaste key, o la key no existe o fue revocada.

**Qué hacer:** Manda `Authorization: Bearer <key>`. Para probar, usa `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 | Reintentar |
|---|---|
| 402 | No |

**Causa:** El saldo no alcanza ni para un ítem. No se cobró.

**Qué hacer:** Compra créditos o sube de plan; el cuerpo trae `balance`, `usd_needed` y el bloque `pricing`.

```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 | Reintentar |
|---|---|
| 402 | No |

**Causa:** Llegaste al tope de gasto mensual de tu cuenta.

**Qué hacer:** Sube el tope en la consola o espera al próximo ciclo (`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 | Reintentar |
|---|---|
| 403 | No |

**Causa:** Tu plan no incluye lo que pediste, por ejemplo un rango de historial completo fuera de su ventana.

**Qué hacer:** Pide un rango dentro de la ventana de tu plan o sube de plan.

```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 | Reintentar |
|---|---|
| 404 | No |

**Causa:** El producto, la oferta, la tienda o el recurso no existe.

**Qué hacer:** Revisa el id; busca con `/v1/products/search` para obtener uno válido.

```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 | Reintentar |
|---|---|
| 429 | Sí, con espera |

**Causa:** Pasaste el límite de consultas por segundo, minuto o día de tu key.

**Qué hacer:** Espera los segundos de `Retry-After` y reintenta con espera creciente.

```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 | Reintentar |
|---|---|
| 501 | No |

**Causa:** El endpoint está planificado y todavía no existe.

**Qué hacer:** Usa la alternativa que indica `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 | Reintentar |
|---|---|
| 404 | No |

**Causa:** Reservado para un país, tienda o categoría que no cubrimos. La API todavía no lo emite: hoy responde 200 con una lista vacía y un aviso en `meta.warnings`.

**Qué hacer:** Revisa `/v1/coverage` para ver qué existe, o pide la cobertura con `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 | Reintentar |
|---|---|
| 500 | Sí, con espera |

**Causa:** Un error nuestro. No se cobró.

**Qué hacer:** Reintenta con espera creciente; si sigue, manda el `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 | Reintentar |
|---|---|
| 503 | Sí, con espera |

**Causa:** El servicio no está disponible por un momento.

**Qué hacer:** Reintenta después de `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"
}
```
