Límites de peticiones
Cuotas por clave, empresa y cartera de la cuenta según el tier. Cabeceras X-RateLimit-* y back-off recomendado.
La API pública evalúa tres ejes de cuota para garantizar la equidad entre tenants y proteger el backend frente a picos:
- API key — ventana deslizante por minuto y volumen mensual.
- Empresa — el agregado de todas las claves de la empresa.
- Cartera de la cuenta — la cuenta a la que pertenece la credencial, solo cuando esa cuenta tiene dos o más NIF reales.
Los límites dependen del tier de tu API key. El tier se deriva del plan
de tu cuenta: nunca se fija por clave ni por petición, no existe una compra
de capacidad independiente y se actualiza automáticamente cuando cambia el
plan. Solo las migraciones históricas grandfathered_addon pueden conservar un
tier superior, y únicamente mientras siga activo un plan válido.
Niveles
| Tier | Clave/min | Clave/mes | Empresa/min | Empresa/mes | Incluido con |
|---|---|---|---|---|---|
| Free | 10 | 100 | 10 | 100 | El trial de 10 días. |
| Starter | 30 | 5.000 | 90 | 15.000 | El plan Esencial. |
| Pro | 300 | 50.000 | 900 | 150.000 | Los planes Profesional y Avanzado. |
| Scale | Según contrato | Según contrato | Según contrato | Según contrato | El plan Enterprise. |
Los límites por clave se aplican. Los agregados por empresa están presentes en
el contrato y, en la versión entrante de la aplicación, funcionan en modo de
observación (shadow): miden los rechazos que habrían ocurrido sin devolver
429. No trates ese modo como capacidad contratada; puede promocionarse a
aplicación efectiva después de calibrarlo sin cambiar la forma de la petición
ni de la respuesta.
Las claves fact_test_ siguen consumiendo su cuota por clave, pero los ejes
agregados de empresa y cartera omiten las empresas sandbox para que el tráfico
de prueba no consuma capacidad agregada de producción.
Ventana deslizante
El cubo por minuto no es una ventana fija de "60 segundos desde las 12:00".
Es una ventana deslizante: en cualquier momento, la API cuenta cuántas
peticiones aceptadas hay en los últimos 60 segundos para tu clave. Cuando el
contador iguala al límite, las peticiones siguientes responden 429 hasta
que pase suficiente tiempo para que las primeras peticiones "salgan" de la ventana.
Por qué: no hay un "minuto de gracia" cada 60 segundos en el que pudieras enviar el doble del límite. Más justo y más estable bajo tráfico real.
Techos de empresa y cartera
Cuando los ejes agregados están en aplicación efectiva, la API evalúa primero
la clave, y los techos por minuto antes que los mensuales. Un rechazo de
empresa usa company_rate_limit_exceeded o
company_monthly_quota_exceeded: añadir claves no eleva esos techos porque se
cuentan todas juntas.
La cartera de la cuenta
El tercer eje es de la cuenta de la credencial, no del NIF que nombra una petición. Una credencial opera una cartera cuando su propia cuenta tiene al menos dos NIF reales. La sandbox de la cuenta no cuenta, así que un NIF titular y su sandbox no son una cartera, y los NIF archivados tampoco cuentan. Con un solo NIF real este eje nunca se aplica: las peticiones siguen los techos de clave y de empresa de arriba y nada más.
Dos reglas deciden dónde cuenta una petición:
- Un NIF alcanzado por una relación cuenta en tu cartera. Una petición sobre un NIF que otra cuenta pone a tu alcance, por un acceso cedido o por una cuenta gestionada, suma en la cartera de la cuenta de tu credencial y en el suelo reservado del NIF que nombra. Nunca suma en la cartera de la cuenta dueña de ese NIF.
- Un NIF que sale de la cuenta cambia de techo. Cuando un NIF pasa a otra cuenta por un traspaso de NIF, las peticiones sobre él dejan de contar en la cartera de la cuenta que abandona y cuentan en la cartera de su cuenta nueva, a través de las credenciales de esa cuenta. Que una cuenta opere o no una cartera se recuerda durante cerca de un minuto, así que un cambio en el número de NIF de una cuenta llega a los techos dentro de ese minuto.
Una cartera tiene un catálogo plano independiente por tier:
| Tier | Banda común/min | Suelo reservado por NIF/min | Cartera/mes | Claves activas en la cuenta |
|---|---|---|---|---|
| Free | 10 | 10 | 100 | 1 |
| Starter | 180 | 20 | 150.000 | 15 |
| Pro | 1.800 | 30 | 1.500.000 | 500 |
| Scale | Según contrato | Según contrato | Según contrato | Según contrato |
El suelo reservado de un NIF se evalúa primero. Un NIF por debajo de su suelo puede seguir aunque la banda común se haya agotado; la banda común no es, por tanto, un total absoluto de la cartera. La cuota mensual no tiene suelo y agrega toda la cartera. Ninguno de los cuatro valores depende de cuántos NIF tenga la cuenta.
Los techos de cartera por minuto y mensual todavía no están en vigor:
funcionan ahora en modo de observación (shadow), pendientes de calibración con
tráfico real de cierre. Miden los rechazos que habrían producido sin devolver
429, y mientras solo observan, el techo por clave sigue aplicándose como
antes.
El tope de claves funciona distinto: se comprueba al crear una clave, no en cada petición, y ya está en vigor. Se aplica a una cartera, porque una cuenta con un solo NIF solo se topa con el tope por empresa, y va además de ese tope, que se comprueba primero. Cuenta todas las credenciales de la cuenta que no se han revocado, incluidas las claves de pruebas. Una clave caducada que nadie revocó sigue ocupando su plaza, así que revoca las claves que ya no uses. Una cuenta que ya supera el tope conserva todas sus claves: solo se rechazan las nuevas.
Los rechazos de cartera usan portfolio_rate_limit_exceeded y
portfolio_monthly_quota_exceeded, y una clave creada por encima del tope
responde 422
portfolio_max_api_keys_exceeded.
Dar de alta más NIF o API keys no eleva un techo plano de cartera; reparte el
trabajo en el tiempo o solicita una ampliación auditada para la empresa.
Cabeceras de respuesta
Toda respuesta (incluido 429) incluye:
| Cabecera | Significado |
|---|---|
X-RateLimit-Limit | Límite por minuto de tu tier. |
X-RateLimit-Remaining | Peticiones restantes en la ventana actual. |
X-RateLimit-Reset | Timestamp UNIX en el que se libera un hueco (una petición sale de la ventana). |
Retry-After | Solo en 429, y nunca en los tres errores de cuota mensual. Segundos hasta que puedas reintentar. Un contador mensual se renueva al empezar el siguiente mes natural. |
Ejemplo de cabeceras en una respuesta 200:
HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1747314060Y en un 429 por minuto:
HTTP/1.1 429 Too Many Requests
Retry-After: 7
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1747314007Código de error
{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Has superado el límite de peticiones. Vuelve a intentarlo en unos segundos.",
"request_id": "req_..."
}
}El code estable identifica qué eje aplicado se ha agotado:
| Eje | Por minuto | Por mes |
|---|---|---|
| API key | rate_limit_exceeded | monthly_quota_exceeded |
| Empresa | company_rate_limit_exceeded | company_monthly_quota_exceeded |
| Cartera de la cuenta | portfolio_rate_limit_exceeded | portfolio_monthly_quota_exceeded |
Todos usan type: rate_limit_error. Respeta Retry-After en los de por minuto.
Los mensuales no llevan Retry-After: el contador se renueva al empezar el
siguiente mes natural, así que esperar unos segundos no los resuelve. Los
fallos de autenticación repetidos se limitan por separado con
code: too_many_auth_failures.
Buenas prácticas
1. Respeta Retry-After
import time, requests
def call_with_retry(url, **kwargs):
while True:
resp = requests.get(url, **kwargs)
if resp.status_code != 429:
return resp
sleep = int(resp.headers.get('Retry-After', 1))
time.sleep(sleep)Los tres errores de cuota mensual no llevan Retry-After. Un 429 mensual no se
resuelve esperando unos segundos: deja de reintentarlo y ramifica según
error.code.
2. Back-off exponencial con jitter
Para 5xx, donde no hay Retry-After:
import random, time
def backoff(attempt):
return min(60, (2 ** attempt) * 0.1 + random.uniform(0, 0.5))
for attempt in range(5):
resp = requests.get(url)
if resp.status_code < 500:
break
time.sleep(backoff(attempt))3. Monitoriza X-RateLimit-Remaining
Si tu integración se acerca de forma consistente al 10% del límite, considera:
- Cambiar a un plan con un tier superior.
- Agrupar en lotes: en lugar de N POSTs, agrega y haz 1 POST.
- Cachear lecturas frecuentes (productos, impuestos, series).
- Suscribirte a webhooks en lugar de hacer polling.
4. Webhooks > polling
Si haces polling de /v1/companies/{company}/invoices?status=paid cada minuto para detectar pagos
consumes 30 rpm solo para eso. Suscríbete al evento invoice.paid
y reduce eso a 0 peticiones.
5. Claves por integración
Si tienes dos integraciones (un dashboard interno + un cron de exportación), crea dos claves distintas: cada clave tiene sus propios cubos por minuto y mensual, así un cron pesado no consume el cubo de la clave del dashboard.
Separar claves no aumenta la capacidad agregada. Los techos de empresa y cartera suman las claves que correspondan; usa más claves para aislamiento y atribución, no para esquivar la cuota.
Cuotas administrativas
Algunas cargas tienen presupuestos de protección independientes de la tasa
de peticiones. No consumen los cubos de clave/empresa/cartera ni aparecen en
X-RateLimit-*:
| Carga | Code estable | Qué se mide |
|---|---|---|
| Envíos de email | email_recipient_budget_exceeded | Destinatarios distintos en la ventana, sumados entre credenciales. |
| Entrega de email | email_delivery_circuit_open | Corte temporal de seguridad del proveedor/empresa tras fallos de entrega; no es una cuota. |
| Cuentas nuevas | account_probation_limit_exceeded | Capacidad reducida por antigüedad hasta que la cuenta madura, verifica su censo AEAT o activa una suscripción de pago. |
| Endpoints PDF | pdf_generation_budget_exceeded | Trabajo de render horario, por empresa o documento (subcode). |
| Importaciones y subidas | upload_in_flight_budget_exceeded | Bytes temporales cruzando el disco a la vez, no almacenamiento ocupado. |
| Exportaciones empaquetadas | export_budget_exceeded | Documentos empaquetados o artefactos generados por hora (subcode). |
Estos codes responden 429 con type: rate_limit_error; usa Retry-After o
error.details.retry_at cuando existan. Cambiar de credencial no evita un
presupuesto de empresa. Un 402 storage_quota_exceeded distinto indica que la
cuenta ha alcanzado su uso máximo; no se libera esperando: escribe a soporte.
Los topes de cantidad de endpoints webhook son capacidad del plan,
no un presupuesto 429; consulta Precios.
Subida de tier
Cambiar de tier no requiere rotar claves. Cuando tu plan cambia:
- Las nuevas cuotas aplican de inmediato.
- La cuota mensual consumida en el tier anterior no se reinicia: solo crece el tope mensual.
- Las claves existentes conservan su
id; el nuevo tier se aplica a todas automáticamente.
Identidad para enviar documentos
El envío de documentos puede requerir identidad acreditada mediante una suscripción activa o una identificación censal aceptada. Un NIF que no es el titular la toma del NIF titular de su cuenta, y los NIF de una cuenta gestionada la toman del NIF titular de la cuenta del proveedor mientras dure la gestión. 422 sender_identity_not_verified requiere corregir esa identidad; un 429 corresponde a un presupuesto de envío distinto. Consulta envío de documentos y la identidad de envío de un NIF.