Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.getzenstep.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Rate limits protect the Zenstep API from abuse and ensure fair resource allocation. Limits are applied per-organisation and per-endpoint bucket.

Limits by endpoint

EndpointBucketLimitWindow
POST /api/v1/eventsevents300 requests60 seconds
GET /api/v1/flowsflows60 requests60 seconds
GET /api/v1/flows/[id]flows60 requests60 seconds
POST /api/v1/flows/[id]/completeflows60 requests60 seconds
GET/POST /api/v1/flows/[id]/checklist-progressflows60 requests60 seconds
POST /api/v1/steps/[id]/fingerprintfingerprint30 requests60 seconds
POST /api/v1/steps/[id]/healthhealth60 requests60 seconds
GET /api/v1/heartbeatheartbeat10 requests60 seconds
GET /api/v1/snippet-statusstatus10 requests60 seconds
The events bucket allows batch requests of up to 50 events each — so the effective throughput is up to 15,000 events per minute per organisation at the maximum batch size.

Rate limit headers

Every response includes rate limit headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 247
X-RateLimit-Reset: 1718000060
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets

Handling 429 responses

When you exceed the rate limit, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1718000060
{
  "error": "Rate limit exceeded",
  "retryAfter": 23
}
Use the Retry-After header value (in seconds) to delay your next request:
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = parseInt(
        response.headers.get("Retry-After") ?? "5",
        10,
      );
      await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
      continue;
    }

    return response;
  }
  throw new Error("Max retries exceeded");
}

Best practices

Batch events — the /api/v1/events endpoint accepts up to 50 events per request. Always batch events from your snippet rather than sending one request per event. Cache flow data — the /api/v1/flows response is stable between publishes. Cache it for the duration of a user session and only re-fetch on SPA navigation or after a configurable TTL (the Zenstep snippet caches for the page lifecycle by default). Backoff on 429 — implement exponential backoff for any integration that calls the API from a backend service.