← Volver a docs

Manejo de errores

Cotizave usa códigos de respuesta HTTP estándar y un formato consistente de errores JSON para que puedas manejar fallos de forma predecible.


Formato de errores

Todos los errores devuelven un JSON con esta estructura:

{
  "code": "string_identificador_del_error",
  "message": "Descripción legible para humanos."
}

Campos:

Códigos de estado HTTP

CódigoSignificadoEjemplos
200OKRequest exitoso
400Bad RequestParámetros inválidos o mal formateados
401UnauthorizedAPI Key faltante o inválida
403ForbiddenPlan insuficiente, key de ambiente incorrecto
404Not FoundRecurso inexistente (ej: market desconocido)
422Unprocessable EntityParámetros válidos en formato pero lógicamente incorrectos
429Too Many RequestsRate limit excedido
500Internal Server ErrorError interno de Cotizave
502Bad GatewayProblema con una fuente upstream
503Service UnavailableMantenimiento o sobrecarga temporal

Errores comunes

400 Bad Request

Request mal formateado o con parámetros inválidos.

{
  "code": "invalid_parameter",
  "message": "Parameter 'from' must be one of: USD, EUR."
}

Cómo responder: revisa los parámetros que estás enviando. Probablemente un typo, un valor no soportado o un formato incorrecto.

401 Unauthorized

Problema con la autenticación.

{
  "code": "unauthorized",
  "message": "Missing or invalid API key."
}

Posibles code específicos:

Cómo responder: verifica tu API Key. Si fue revocada, crea una nueva.

403 Forbidden

La autenticación es válida pero no tienes permisos para este endpoint.

{
  "code": "plan_insufficient",
  "message": "This endpoint requires a paid plan. Current plan: free."
}

Posibles code específicos:

Cómo responder: haz upgrade del Plan si necesitas el endpoint, o usa una funcionalidad equivalente disponible en tu Plan actual.

404 Not Found

El recurso solicitado no existe.

{
  "code": "market_not_found",
  "message": "Market 'xyz' not found."
}

Cómo responder: verifica el nombre del market. Consulta la lista completa de markets disponibles en la documentación del endpoint.

422 Unprocessable Entity

Los parámetros son válidos en formato pero no tienen sentido lógico.

{
  "code": "invalid_amount",
  "message": "Amount must be greater than 0."
}

Cómo responder: revisa la lógica de tu request.

429 Too Many Requests

Rate limit excedido.

{
  "code": "rate_limit_exceeded",
  "message": "Monthly quota exceeded."
}

Headers relevantes:

Posibles code específicos:

Cómo responder:

  1. Respeta el header Retry-After (valor en segundos)
  2. Implementa backoff exponencial
  3. Considera hacer upgrade del Plan si es recurrente

500 Internal Server Error

Error inesperado del lado de Cotizave. No es tu culpa.

{
  "code": "internal_error",
  "message": "An unexpected error occurred. Please try again."
}

Cómo responder:

  1. Implementa retry con backoff exponencial
  2. Si persiste por más de unos minutos, revisa /status
  3. Si el status page dice “operational” pero sigues viendo errores, contáctanos en hello@cotizave.com

502 Bad Gateway

Problema con una fuente upstream. Por ejemplo, si Binance o el sitio del BCV están caídos.

{
  "code": "upstream_error",
  "message": "Upstream source 'binance' is currently unavailable."
}

Cómo responder: si pides una market específica que está caída, prueba con otra o usa /v1/fx/rates (devuelve las que sí están disponibles).

503 Service Unavailable

Servicio temporalmente no disponible. Mantenimiento planificado o sobrecarga.

{
  "code": "service_unavailable",
  "message": "Service is temporarily unavailable. Please try again shortly."
}

Cómo responder: retry con el delay sugerido.

Patrón recomendado: retry con backoff exponencial

Para manejar errores transitorios (5xx, 429 con Retry-After):

async function fetchWithRetry(url, options, maxRetries = 5) {
  let lastError

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options)

      // Éxito
      if (response.ok) {
        return await response.json()
      }

      // Errores que no tiene sentido retryar
      if (response.status === 401 || response.status === 403 || response.status === 404) {
        throw new Error(`Non-retryable: ${response.status}`)
      }

      // Rate limit: respetar Retry-After
      if (response.status === 429) {
        const retryAfter = parseInt(response.headers.get('Retry-After') || '60')
        await sleep(retryAfter * 1000)
        continue
      }

      // Errores retriables (5xx)
      if (response.status >= 500) {
        const delay = Math.min(1000 * Math.pow(2, attempt), 30000) // max 30s
        await sleep(delay)
        continue
      }

      // Otros errores
      throw new Error(`HTTP ${response.status}`)

    } catch (err) {
      lastError = err
      if (attempt === maxRetries - 1) throw err
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000)
      await sleep(delay)
    }
  }

  throw lastError
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

Buenas prácticas:

Verificación de salud

Si quieres verificar que la API está operativa antes de hacer requests críticos, puedes usar el endpoint de healthcheck:

curl https://api.cotizave.com/health

Respuesta normal:

{
  "status": "ok",
  "timestamp": "2026-04-08T17:30:00Z",
  "version": "1.0.0"
}

Este endpoint no requiere autenticación y no cuenta contra tu cuota.

Reporte de bugs

Si encuentras un comportamiento que parece un bug (response con schema roto, código de error inconsistente, datos corruptos), repórtalo a hello@cotizave.com con:

Respondemos dentro de 24 horas hábiles.