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 default tier on key
creation is controlled from Developers > API Keys > Settings > Default tier, and can be overridden per key from the dashboard.
Tiers
| Tier | Per minute | Per month | Cost |
|---|---|---|---|
| Free | 10 rpm | 100 requests | Included in any plan. |
| Starter | 30 rpm | 5,000 | developer_api Starter add-on (€4.90/mo). |
| Pro | 300 rpm | 50,000 | developer_api Pro add-on (€19.90/mo). |
| Scale | Custom | Custom | Contact sales. |
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.
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 tier
If you have two integrations (an internal dashboard + an export cron), create two distinct keys with tiers sized for each one. That way 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. After subscribing to a higher add-on:
- New quotas apply immediately.
- The monthly quota consumed at the previous tier does not reset: only the monthly cap grows.
- Existing keys keep their
idand tier; manage tiers from the dashboard (Developers > API Keys).