Límites, cuotas e idempotencia
Tres controles distintos: rate limit por llave (peticiones por minuto), cuota mensual por cuenta (unidades consumidas) y límites de payload. Entender cuál aplica evita reintentos inútiles.
Rate limit por llave
El límite se aplica por llave de API, no por IP, para no castigar a clientes detrás de NAT. Usa un token bucket con la tasa del plan y una ráfaga adicional:
capacidad = rate_limit_rpm + burst
recarga = rate_limit_rpm / 60 peticiones por segundo
Invitación · Beta: capacidad 70, recarga 1 petición/s| Plan | Unidades / mes | Rate limit | Empresas | Precio |
|---|---|---|---|---|
| Invitación · Beta tu plan | 2,000 | 60 rpm + 10 burst | 5 | Sin costo (beta) |
| Starter | 5,000 | 120 rpm + 20 burst | 15 | $1,490 MXN / mes (lista) |
| Growth | 25,000 | 300 rpm + 40 burst | 60 | $5,900 MXN / mes (lista) |
| Enterprise | 100,000 | 900 rpm + 120 burst | 500 | Cotización |
X-RateLimit-* y X-Quota-* se están habilitando por entorno: si no aparecen en una respuesta, no dependas de ellas; usa el 429 y Retry-After como fuente de verdad. El consumo sí se mide desde el primer día.Cabeceras de límite y cuota
| Cabecera | Descripción |
|---|---|
X-RateLimit-Limit | Capacidad del bucket (rpm + burst). |
X-RateLimit-Remaining | Peticiones disponibles en este momento. |
X-RateLimit-Reset | Segundos para recuperar la siguiente petición. |
Retry-After | Segundos que debes esperar antes de reintentar (en 429). |
X-Quota-Limit | Unidades incluidas en el periodo mensual. |
X-Quota-Used | Unidades consumidas en el periodo. |
X-Quota-Remaining | Unidades disponibles antes del corte. |
X-Request-Id | Identificador de la petición; úsalo en soporte. |
X-Response-Time | Tiempo de proceso en milisegundos. |
X-API-Version | Versión del contrato (v1). |
Cuando superas el rate limit, la respuesta es:
HTTP/1.1 429 Too Many Requests
Retry-After: 7
Content-Type: application/problem+json
X-Request-Id: req_b41c8e2a7f093d65
{
"type": "https://api.434alchemycode.com/docs/errors/rate_limited",
"title": "Demasiadas peticiones",
"status": 429,
"code": "RATE_LIMITED",
"detail": "Superaste el límite por minuto. Respeta `Retry-After`.",
"retryable": true,
"request_id": "req_b41c8e2a7f093d65"
}Cuota mensual
La cuota se mide en unidades, no en peticiones. Cada servicio declara cuántas consume; el buzón IMSS
consume 2 porque sincroniza y descarga evidencias. La API evalúa la cuota antes de encolar el job: si no
hay unidades, responde 429 QUOTA_EXCEEDED sin consumir ni crear job.
| Servicio | Key | Unidades | Modo |
|---|---|---|---|
| Constancia SAT | sat.constancia | 1 | Asíncrono |
| Opinión 32-D | sat.opinion | 1 | Asíncrono |
| Domicilio fiscal | sat.domicilio | 1 | Asíncrono |
| Buzón IMSS | imss.buzon | 2 | Asíncrono |
| Opinión IMSS | imss.opinion | 1 | Asíncrono |
| Opinión Infonavit | infonavit.opinion | 1 | Asíncrono |
| Artículo 69-B | sat.articulo69b | 1 | Síncrono |
| CAPTCHA | captcha.resolve | 1 | Síncrono |
- Corte mensual: el periodo es el mes calendario en UTC;
resets_atindica el inicio del siguiente. - Sin arrastre: las unidades no usadas no se acumulan al mes siguiente.
- Errores no cobran: un job fallido no consume unidades; un job exitoso sí, aunque el sentido de la opinión sea negativo.
- Sandbox también mide: las consultas con
ck_test_consumen cuota (con datos simulados). - Sin cargo en beta: las unidades se miden, pero no se genera cobro durante la beta.
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
{
"type": "https://api.434alchemycode.com/docs/errors/quota_exceeded",
"title": "Cuota mensual agotada",
"status": 429,
"code": "QUOTA_EXCEEDED",
"detail": "Usaste 2000 de 2000 consultas del periodo. La cuota se renueva el 2026-11-01.",
"retryable": false,
"request_id": "req_7c1f9a3e5b2d8064",
"quota": {
"period_start": "2026-10-01T00:00:00+00:00",
"period_end": "2026-11-01T00:00:00+00:00",
"quota": 2000,
"used": 2000,
"remaining": 0,
"percent": 100.0,
"exceeded": true,
"resets_at": "2026-11-01T00:00:00+00:00"
}
}Consulta tu estado en GET /v1/me (cuota y plan) y el detalle en GET /v1/usage
(consumo por servicio, empresa, día y tokens de IA).
Idempotencia
Los seis endpoints asíncronos aceptan el encabezado Idempotency-Key. Sirve para que un
reintento de red no cree dos jobs ni cobre dos veces.
curl -sS -X POST "https://api.434alchemycode.com/v1/sat/constancias" \
-H "Authorization: Bearer $ALCHEMY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2c7a-3d0e-4a51-9b0e-5c2a7f9d1e44" \
-d '{"company_id": "9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17"}' | Situación | Respuesta |
|---|---|
| Primera petición con la clave | 202 y job nuevo. |
| Reintento con la misma clave y el mismo cuerpo | 200 con el job actual (se refresca su estado, no el snapshot del primer 202). |
| Misma clave, cuerpo distinto | 409 IDEMPOTENCY_CONFLICT. |
| Misma clave mientras la petición original sigue en proceso | 409 IDEMPOTENCY_IN_PROGRESS (reintentar en segundos). |
| Después de 24 horas | La clave se libera: la misma clave crea un job nuevo. |
Usa un UUID v4 por operación de negocio y guárdalo junto con tu registro. No uses la misma clave para consultas distintas ni la reutilices entre empresas.
Tamaño de payload
| Límite | Valor | Error |
|---|---|---|
| Cuerpo de la petición | 2 MB | 413 PAYLOAD_TOO_LARGE |
| Cada archivo de e.firma | 10 MB | 413 PAYLOAD_TOO_LARGE |
| Lote de RFC (69-B) | 5,000 RFC | 422 VALIDATION_ERROR |
| Imagen de CAPTCHA | 2 MB en base64 | 413 PAYLOAD_TOO_LARGE |
| Página de listado | per_page máx. 100 | 422 VALIDATION_ERROR |
Content-Length. Si tu cliente usa chunked encoding sin longitud, el límite se aplica al leer el cuerpo: envía siempre Content-Length.Buenas prácticas
- Respeta
Retry-After; no reintentes en paralelo tras un 429. - Limita la concurrencia de tu cliente (4–8 en beta) y encola lo que exceda.
- Mide el consumo con
/v1/usage; alerta al 80 % de la cuota. - Reutiliza
Idempotency-Keypor operación de negocio, no por reintento manual. - Divide lotes de 69-B en bloques de 1,000–2,000 RFC para controlar latencia y memoria.
- No uses sandbox para estimar cuota de producción: los datos son simulados, pero las unidades se descuentan.