Rate Limits

Per-key request budgets and how to handle 429s.

Each API key has a per-minute request budget. Exceeding it returns 429 Too Many Requests.

Default budgets

ModeBudget
ezp_test_*60 requests / minute
ezp_live_*600 requests / minute

Need more? Contact support — limits can be raised per tenant.

Response headers

Every response (success or error) includes:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 587
X-RateLimit-Reset: 1746450123
  • X-RateLimit-Limit — your budget for the current window.
  • X-RateLimit-Remaining — calls left in the window.
  • X-RateLimit-Reset — Unix epoch seconds at which the window resets.

Handling 429

When you hit the limit you receive:

HTTP/1.1 429 Too Many Requests
Retry-After: 18
Content-Type: application/json
 
{
  "error": { "code": "rate_limited", "message": "Rate limit exceeded.", "request_id": "req_..." }
}

Always honor the Retry-After header (in seconds). A simple client looks like:

async function callWithBackoff(req: () => Promise<Response>) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await req();
    if (res.status !== 429) return res;
    const retry = Number(res.headers.get('Retry-After') ?? '1');
    await new Promise((r) => setTimeout(r, retry * 1000));
  }
  throw new Error('Rate limited after 5 retries');
}

Tips

  • Batch operations where possible (e.g. POST /v1/imports for up to 1,000 invoices in one request).
  • Cache GET responses on your side using the resource's updated_at timestamp.
  • Use webhooks instead of polling.