QAPI está en beta — la API puede cambiar sin previo aviso. Usa sandbox para pruebas.
QAPIQAPIDocs · by DTEVIA
Guías

Rate Limiting

Límites de velocidad por plan, headers de respuesta y estrategias de retry en QAPI.

Rate Limiting

QAPI aplica límites de velocidad por API key para garantizar disponibilidad a todos los tenants.

Límites por plan

PlanDTEs/mesRate limitPrecio
Starter50060 req/min$59.99
Business2,000200 req/min$199.99
EnterpriseCustomConfigurableCotización

Los usuarios autenticados con JWT tienen un límite de 300 req/min independientemente del plan.

Headers de rate limit

Cada respuesta incluye estos headers:

Prop

Type

Respuesta cuando se supera el límite

Ejemplo HTTP

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 43
Content-Type: application/json

{
  "type": "https://docs.dtevia.com.gt/errors/rate-limited",
  "title": "RATE_LIMITED",
  "status": 429,
  "detail": "Límite de requests excedido. Reintentar en 43s",
  "instance": "/v1/invoices"
}

Estrategia de retry con backoff exponencial

Implementación

async function fetchWithRetry(
  url: string,
  options: RequestInit,
  maxRetries = 3
): Promise<Response> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(url, options);

    if (res.status !== 429) return res;

    if (attempt === maxRetries) throw new Error('Max retries reached');

    const retryAfter = parseInt(res.headers.get('retry-after') ?? '60', 10);
    console.log(`Rate limited. Reintentando en ${retryAfter}s...`);
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }

  throw new Error('Unreachable');
}
import axiosRetry from 'axios-retry';

axiosRetry(axios, {
  retries: 3,
  retryCondition: (error) => error.response?.status === 429,
  retryDelay: (retryCount, error) => {
    const retryAfter = parseInt(
      error.response?.headers['retry-after'] ?? '60',
      10
    );
    return retryAfter * 1000;
  },
});
import time

def fetch_with_retry(url: str, max_retries: int = 3, **kwargs) -> httpx.Response:
    for attempt in range(max_retries + 1):
        res = httpx.post(url, **kwargs)

        if res.status_code != 429:
            return res

        if attempt == max_retries:
            raise Exception("Max retries alcanzados")

        retry_after = int(res.headers.get("retry-after", "60"))
        print(f"Rate limited. Reintentando en {retry_after}s...")
        time.sleep(retry_after)

    raise Exception("Unreachable")

Errores de límite de cuota

Rate limit vs otros límites del plan

Distintos de rate limiting (velocidad), existen otros límites por plan:

ErrorCausa
RATE_LIMITED (429)Demasiadas requests por minuto
SANDBOX_LIMIT_REACHED (429)Superaste el límite diario de facturas de prueba por tipo de DTE
PROJECT_LIMIT_REACHED (429)Alcanzaste el límite de proyectos activos de tu plan
MONTHLY_QUOTA_EXCEEDED (429)El proyecto agotó su cuota mensual de DTEs asignada (solo live)

PROJECT_LIMIT_REACHED es del dashboard, no de facturación

Este error ocurre al crear proyectos en el dashboard cuando llegaste al máximo de proyectos activos de tu plan (starter: 3, business: 8, enterprise: sin límite). No ocurre al emitir facturas. Para resolverlo: archiva un proyecto que no uses o actualiza tu plan.

Buenas prácticas

  1. Lee X-RateLimit-Remaining y ralentiza proactivamente cuando se acerque a 0
  2. Usa colas para emisión masiva en lugar de enviar en ráfaga
  3. Implementa backoff que respeta el Retry-After exacto (no adivines el tiempo)
  4. Guarda X-Request-ID en tu log para cada factura — facilita el soporte
  5. Distribuye la carga a lo largo del minuto si tienes muchas facturas simultáneas

On this page