ZapAPI Docs
Get StartedConceptsAI GatewayGuidesPayment GatewaySDKAPI ReferenceResources
Concepts

Rate Limits

Per-key and per-IP rate limits on the ZapAPI API — headers, defaults, and how to handle 429s gracefully.

ZapAPI applies rate limits to protect the platform and ensure fair use. Limits are applied per API key (and per IP for public endpoints) and are returned in standard RateLimit-* headers on every response.

Default limits

ScopeLimit
zap_test_ keys100 requests / minute
zap_live_ keys600 requests / minute
Invoice creation (POST /invoices)120 requests / minute
Public LNURL endpoints300 requests / minute per IP
Public payment-link pages600 requests / minute per IP

If you need a higher limit for a legitimate workload (e.g., a high-volume marketplace), contact support — we can raise limits per project.

Rate limit headers

Every response carries the standard IETF RateLimit-* headers:

RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 45
  • RateLimit-Limit — the ceiling for the current window.
  • RateLimit-Remaining — requests left in the current window.
  • RateLimit-Reset — seconds until the window resets.

When you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header:

HTTP/1.1 429 Too Many Requests
Retry-After: 23
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 23
{
  "status": "error",
  "statusCode": 429,
  "code": "rate_limited",
  "message": "Rate limit exceeded. Retry after 23 seconds."
}

Handling 429s

The TypeScript SDK surfaces rate limiting via ZapAPIError.isRateLimited:

try {
  await zap.invoices.create(params);
} catch (err) {
  if (err instanceof ZapAPIError && err.isRateLimited) {
    const retryAfter = Number(err.payload.retryAfter ?? 1);
    await sleep(retryAfter * 1000);
    return retry();
  }
  throw err;
}

For bulk workloads, proactively throttle by watching RateLimit-Remaining and slowing down as it approaches zero, rather than reacting to 429s after the fact.

Best practices

  • Use webhooks, not polling. Polling GET /invoices/:id to detect payment is the most common way to burn through a rate limit. Subscribe to invoice.paid instead — see Webhooks.
  • Batch where possible. List endpoints return up to 100 records per request — prefer one list call over many get calls.
  • Cache aggressively. Public LNURL params and payment-link metadata are cacheable; respect the Cache-Control headers.
  • Use idempotency keys. Retrying a failed request with the same Idempotency-Key does not count against your limit twice.

IP-based limits

Public endpoints (LNURL params, hosted checkout, public payment links) are limited per IP rather than per key. If your users share NAT egress (corporate networks, mobile carriers), they may share a budget — design public pages to degrade gracefully under 429s.

On this page