ReferenceAPI Reference

Rate Limits

Per-plan request quotas, response headers, backoff strategy, and how to design integrations that never hit a limit.

3 min read

Rate limits keep the platform responsive for everyone. Designed well, an integration should rarely see one.

Limits

PlanAuthenticated requestsBurst
Solo300 / minute60
Operator600 / minute120
Agency1,200 / minute240
EnterpriseNegotiatedNegotiated
Endpoint classLimit
Public catalog (/api/public/*, unauthenticated)120 / minute per IP
Public booking creation20 / minute per IP
Authentication endpoints10 / minute per IP
AI generation endpoints20 / minute per workspace
Bulk export endpoints10 / minute per workspace

Limits apply per credential for authenticated calls and per IP for public ones. Webhook deliveries to you do not consume your quota.

Response headers

Every response carries the current state:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 583
X-RateLimit-Reset: 1751362500

X-RateLimit-Reset is a Unix timestamp for when the window resets.

When you exceed a limit

HTTP/1.1 429 Too Many Requests
Retry-After: 24
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 24 seconds."
  }
}

Honour Retry-After. Do not retry immediately — it extends the throttle.

Backoff

Exponential backoff with jitter, capped, with a retry ceiling:

async function requestWithBackoff(
  url: string,
  init: RequestInit,
  maxAttempts = 5,
): Promise<Response> {
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
    const response = await fetch(url, init);

    if (response.status !== 429 && response.status < 500) return response;
    if (attempt === maxAttempts - 1) return response;

    const retryAfter = Number(response.headers.get("Retry-After"));
    const backoff = Number.isFinite(retryAfter) && retryAfter > 0
      ? retryAfter * 1000
      : Math.min(2 ** attempt * 1000, 30_000);

    // Jitter prevents synchronised clients from retrying in lockstep.
    await new Promise((resolve) => setTimeout(resolve, backoff + Math.random() * 500));
  }

  throw new Error("Exhausted retries");
}

Retry 429 and 5xx. Never retry 400, 401, 403, 404, or 422 — the same request will fail the same way.

Designing to stay under the limit

Use webhooks instead of polling. A poll every 10 seconds burns 6 requests a minute forever and still lags reality. A webhook fires once, immediately.

Paginate with a sensible page size. limit=100 moves the same data in a quarter of the requests of limit=25.

Filter server-side. ?status=CONFIRMED&amp;from=2026-07-01 beats fetching everything and filtering in your code.

Cache what rarely changes. Tour catalog and workspace settings can be cached for minutes; availability and bookings cannot.

Batch scheduled work. Run a nightly sync as one paginated sweep, not thousands of per-record calls.

Spread scheduled jobs. If every integration fires at exactly 00:00, they collide. Add a random offset.

Concurrency

Keep concurrent in-flight requests per credential modest — 10 or fewer is comfortable for most integrations. High parallelism plus retries is the fastest route to a sustained throttle.

Bulk operations

For migrations and large imports, contact us before you start. We can advise on sequencing and, for enterprise plans, arrange a temporary elevated limit. Running a migration against production limits without warning usually ends in a throttle mid-import.

Monitoring

Track X-RateLimit-Remaining and alert when it drops below 20% of the limit — that gives you time to fix the pattern before it becomes a 429 in production. Log every 429 with its endpoint so you can find the hot path.

Turn your travel business into an AI-native operation.

Launch direct bookings, operations, CRM, AI itineraries, and enterprise controls from one modern platform.