Rate limits
The API Connect limits the number of requests per API key in two simultaneous time windows.
The limits
| Window | Default limit |
|---|---|
| Per minute | 120 requests |
| Per hour | 5,000 requests |
Both windows are evaluated on every request. If either is exceeded, the API responds with HTTP 429.
These are default values, sized for supermarkets and retail operations with multiple concurrent POS terminals. If your use case requires higher volumes (bulk batch issuance, intensive reporting), contact Ocote to adjust the limits of your key.
Why two windows
- The per-minute window protects against accidental spikes — a runaway loop emitting thousands of invoices per second.
- The per-hour window protects against sustained abuse and guarantees fair capacity across API clients.
A normal issuance flow (one invoice when a customer pays) never touches either.
Control headers
Every response from the API Connect (including errors) includes these headers:
| Header | Description |
|---|---|
X-RateLimit-Limit-Minute | Your per-minute limit. Ex: 120. |
X-RateLimit-Limit-Hour | Your per-hour limit. Ex: 5000. |
X-RateLimit-Remaining-Minute | Requests available in the current 60-s window. |
X-RateLimit-Remaining-Hour | Requests available in the current 1-h window. |
X-RateLimit-Reset | Seconds until the minute window resets. |
Real example:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit-Minute: 120
X-RateLimit-Limit-Hour: 5000
X-RateLimit-Remaining-Minute: 118
X-RateLimit-Remaining-Hour: 4982
X-RateLimit-Reset: 42
Read them from your client to pace yourself. If Remaining-Minute drops to 10, that's a good signal that you need to slow down.
When you exceed the limit
HTTP 429 with a Retry-After header indicating seconds:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 18
X-RateLimit-Limit-Minute: 120
X-RateLimit-Remaining-Minute: 0
X-RateLimit-Reset: 18
{
"detail": "Limite de 120 requests por minuto excedido"
}
Rule: wait exactly Retry-After seconds before retrying. Any request before that will also receive 429.
def request_with_backoff(method, url, **kwargs):
for _ in range(5):
r = requests.request(method, url, **kwargs)
if r.status_code == 429:
retry_after = int(r.headers.get("Retry-After", 60))
time.sleep(retry_after)
continue
return r
raise Exception("Rate limit not yielding after 5 attempts")
async function requestWithBackoff(config) {
for (let i = 0; i < 5; i++) {
try {
return await axios(config);
} catch (err) {
if (err.response?.status === 429) {
const retry = parseInt(err.response.headers['retry-after'] || '60');
await new Promise(r => setTimeout(r, retry * 1000));
continue;
}
throw err;
}
}
throw new Error('Rate limit not yielding after 5 attempts');
}
What counts against the limit
| Response type | Counts against limit? |
|---|---|
| HTTP 200 (success, MH rejection, contingency) | Yes |
| HTTP 400 (invalid payload) | Yes |
| HTTP 401 (invalid API key) | No |
| HTTP 404 (document not found) | Yes |
| HTTP 429 (rate limit) | No |
| HTTP 500 (internal Ocote error) | Yes |
401s do not count because they require no API work and it would not make sense to rate-limit credential-stuffing attacks this way. 429s do not count because it would be contradictory.
Queries (GET /status/{id}, GET /file/{id}) count the same as issuances.
Best practices
Group polling. If you're monitoring N documents in contingency, do it once an hour reviewing all of them — not each document every minute. See Contingency > Responsible polling.
Do not retry 429 without waiting. An immediate retry on 429 does not speed anything up — it keeps you in 429.
Use Remaining-Minute to slow down proactively. If you drop below 10, insert a sleep(X-RateLimit-Reset + 1) before the next request.
Batch processing: if you need to emit 500 invoices in one shot, send them with ~500 ms pauses between each to stay well below 120/minute. At that pace you would take 4 minutes for the 500; if you try to go full-speed you end up waiting longer due to 429s.
Monitor headers in production. Dashboard X-RateLimit-Remaining-Hour from your responses to detect trends before they hit.
Non-numeric limits
Besides quantitative rate limiting, the API has qualitative payload limits:
lines[]per document: max 2,000 lines.- Line description: max 1,000 characters.
external_ref: max 100 characters.- Total JSON body length: max 5 MB.
These are enforced at validation (HTTP 400), not at rate limit. Exceeding any of them returns a payload error with specific detail.
See also
- Responses and states — HTTP code semantics.
- Retry and idempotency — how to retry safely (including 429).