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:
- Per-minute quota (sliding window).
- 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
| Tier | Per minute | Per month | Included with |
|---|---|---|---|
| Free | 10 rpm | 100 requests | The 10-day trial. |
| Starter | 30 rpm | 5,000 | The Emprendedor plan. |
| Pro | 300 rpm | 50,000 | The Empresario plan. |
| Scale | Custom | Custom | The 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:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Per-minute limit of your tier. |
X-RateLimit-Remaining | Requests remaining in the current window. |
X-RateLimit-Reset | UNIX timestamp when a slot frees up (one slot exits the window). |
Retry-After | Only 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: 1747314060And on a 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 7
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1747314007Error 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:
| Endpoint | Quota |
|---|---|
POST /v1/webhook_endpoints | Limited number of endpoints per company. |
POST /v1/invoices/{id}/send | Rate-limited per invoice to avoid duplicate emails. |
GET /v1/invoices/{id}/pdf | Rate-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):
- New quotas apply immediately.
- The monthly quota consumed at the previous tier does not reset: only the monthly cap grows.
- Existing keys keep their
id; the new tier applies to all of them automatically.