Límites de peticiones
Cuotas por clave, empresa y cartera gestionada 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 gestionada — la empresa gestora y todas sus hijas activas, solo cuando la credencial pertenece a una cartera.
Los límites dependen del tier de tu API key. El tier se deriva del plan
de Factuarea de tu empresa: 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 la empresa mantenga
un plan activo 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 Emprendedor. |
| Pro | 300 | 50.000 | 900 | 150.000 | El plan Empresario. |
| Scale | 1.200 | 5.000.000 | 3.600 | 15.000.000 | 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, después la empresa y por último la cartera gestionada. 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.
Una cartera gestionada tiene un catálogo plano independiente por tier:
| Tier | Banda común/min | Suelo reservado por empresa activa/min | Cartera/mes | Claves activas en cartera |
|---|---|---|---|---|
| Free | 10 | 10 | 100 | 1 |
| Starter | 180 | 20 | 150.000 | 15 |
| Pro | 1.800 | 30 | 1.500.000 | 500 |
| Scale | 7.200 | 60 | 50.000.000 | 2.000 |
El suelo reservado por empresa se evalúa primero. Una hija 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. Estos valores también están actualmente en modo
shadow, pendientes de calibración con tráfico real de cierre.
Los rechazos de cartera usan portfolio_rate_limit_exceeded y
portfolio_monthly_quota_exceeded. Crear más empresas hijas 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. Segundos hasta que puedas reintentar. |
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:
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 | rate_limit_exceeded |
| Empresa | company_rate_limit_exceeded | company_monthly_quota_exceeded |
| Cartera gestionada | portfolio_rate_limit_exceeded | portfolio_monthly_quota_exceeded |
Todos usan type: rate_limit_error. Respeta Retry-After cuando aparezca; los
errores de cartera también publican el instante o los segundos de reintento en
error.details. 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)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/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 el
almacenamiento persistente agregado de la empresa está lleno y no se libera
esperando. 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. Las empresas gestionadas usan la identidad de la gestoría. 422 sender_identity_not_verified requiere corregir esa identidad; un 429 corresponde a un presupuesto de envío distinto. Consulta envío de documentos.