Factuarea API

Rate limits

Per-minute and monthly quotas per tier. X-RateLimit-* headers and recommended back-off.

The public API enforces two quota levels to guarantee fairness between tenants and protect the backend from bursts:

  1. Per-minute quota (sliding window).
  2. Monthly quota (natural calendar, resets on day 1 at 00:00 Europe/Madrid).

The limits depend on your API key tier. The tier is derived from your company's Factuarea plan (or from an active capacity boost when higher) — it is never set per key or per request, and it updates automatically when your plan changes.

Tiers

TierPer minutePer monthIncluded with
Free10 rpm100 requestsThe 10-day trial.
Starter30 rpm5,000The Emprendedor plan.
Pro300 rpm50,000The Empresario plan.
ScaleCustomCustomThe Enterprise plan (or a capacity boost).

Tiers are cumulative: once the monthly quota is exhausted you get 429 rate_limit_exceeded until day 1 of the next month. The per-minute quota resets on a sliding window.

Capacity boost

If you need more API capacity without changing plans, subscribe to a capacity boost from the dashboard: a tier strictly higher than the one your plan grants (for example, Starter → Pro). While the boost is active, all your keys use the boosted tier. Buying a tier equal to or lower than your plan's returns 422 boost_not_applicable.

Sliding window

The per-minute bucket is not a fixed window "60 seconds since 12:00". It's a sliding window: at any point, the API counts how many accepted requests there are in the last 60 seconds for your key. When the counter equals the limit, subsequent requests respond 429 until enough time has passed for the early requests to "drop off" the window.

Why: there's no "grace minute" every 60 seconds where you could send twice the limit. Fairer and more stable under real traffic.

Response headers

Every response (including 429) includes:

HeaderMeaning
X-RateLimit-LimitPer-minute limit of your tier.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUNIX timestamp when a slot frees up (one slot exits the window).
Retry-AfterOnly on 429. Seconds until you can retry.

Example headers on a 200 response:

HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1747314060

And on a 429:

HTTP/1.1 429 Too Many Requests
Retry-After: 7
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1747314007

Error code

{
  "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_..."
  }
}

Exceeding the per-minute or monthly quota responds 429 with type: rate_limit_error and code: rate_limit_exceeded. The Retry-After header (and the message) tell you how long to wait. Repeated authentication failures are throttled separately with code: too_many_auth_failures.

Best practices

1. Respect 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. Exponential back-off with jitter

For 5xx, where there's no 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. Monitor X-RateLimit-Remaining

If your integration consistently approaches 10% of the limit, consider:

  • Upgrading tier.
  • Batching: instead of N POSTs, aggregate and do 1 POST.
  • Caching frequent reads (products, taxes, series).
  • Subscribing to webhooks instead of polling.

4. Webhooks > polling

If you poll /v1/invoices?status=paid every minute to detect payments you consume 30 rpm just for that. Subscribe to the invoice.paid event and drop that to 0 requests.

5. Per-integration keys

If you have two integrations (an internal dashboard + an export cron), create two distinct keys: each key has its own per-minute and monthly buckets, so a heavy cron doesn't exhaust the budget of an interactive dashboard.

Administrative quotas

Some endpoints have additional quotas independent of the main rate limit:

EndpointQuota
POST /v1/webhook_endpointsLimited number of endpoints per company.
POST /v1/invoices/{id}/sendRate-limited per invoice to avoid duplicate emails.
GET /v1/invoices/{id}/pdfRate-limited PDF generations per minute.

These limits respond 429 with type: rate_limit_error and a specific message.

Tier upgrade

Changing tier does not require rotating keys. When your plan changes (or a capacity boost activates):

  1. New quotas apply immediately.
  2. The monthly quota consumed at the previous tier does not reset: only the monthly cap grows.
  3. Existing keys keep their id; the new tier applies to all of them automatically.

On this page