Docs
Concepts

Rate limits

How many requests a minute your organisation can make in each mode and plan, the headers that tell you where you stand, and how to back off.

Preview· P9

This is a published preview. Names and fields may still change before general availability; changes will be listed in the changelog.

Agoo limits how many requests your organisation can make each minute, separately for live and test mode. Every key and OAuth token for the organisation in that mode shares the same allowance, including publishable keys used by your booking pages, so a busy batch job can slow down your other integrations.

Limits

ModePlanRequests per minute
TestEvery plan100
LiveGrowth (webhook endpoints and events only)100
LivePro600
LiveEnterprise3,000, and can be raised
LiveFree, StarterNo live API access (plan_required)

Enterprise organisations that need more can ask their success manager for a higher limit.

Headers

Every response tells you where you stand:

HeaderMeaning
RateLimit-LimitRequests allowed per minute for your organisation in this mode
RateLimit-RemainingRequests left in the current window
RateLimit-ResetSeconds until the window resets
HTTP/1.1 200 OK
Content-Type: application/json
Agoo-Request-Id: req_01m4ww0hx8f0wa8fyh0evr8050
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 42

When you go over the limit, Agoo responds with 429 and the rate_limited code, plus a Retry-After header with the number of seconds to wait:

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 12
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 12

Back off and retry

Wait for Retry-After before retrying. For internal_error, unavailable and network errors, which have no reliable Retry-After, wait longer after each failure (exponential backoff) and add randomness (jitter) so many clients don't retry at the same instant. Reuse the same Idempotency-Key on every retry of a POST.

agoo-fetch.ts
const RETRYABLE = new Set([429, 500, 503])
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

/** fetch() for the Agoo API that waits and retries on 429, 500 and 503. */
export async function agooFetch(path: string, init: RequestInit = {}, maxAttempts = 5): Promise<Response> {
  const headers = new Headers(init.headers)
  headers.set("Authorization", `Bearer ${process.env.AGOO_API_KEY}`)
  // One key for every attempt of this POST, so a retry never repeats work
  if (init.method === "POST" && !headers.has("Idempotency-Key")) headers.set("Idempotency-Key", crypto.randomUUID())

  for (let attempt = 1; ; attempt++) {
    const res = await fetch(`https://api.agoo.ardent.africa/v1${path}`, { ...init, headers })
    if (!RETRYABLE.has(res.status) || attempt === maxAttempts) return res

    const retryAfter = Number(res.headers.get("retry-after"))
    const backoff = Math.min(30, 2 ** attempt) * (0.5 + Math.random() / 2) // 1–2 s, 2–4 s, 4–8 s … up to 30 s
    await res.body?.cancel()
    await sleep(1000 * (retryAfter > 0 ? retryAfter : backoff))
  }
}

Stay well under the limit

  • Use webhooks instead of polling. One webhook per check-in replaces a loop that asks "anyone new?" every few seconds.
  • Filter on the server. Ask for GET /visits?status=checked_in&site_id=… rather than fetching everything and filtering yourself.
  • Use limit=100 when you page through large lists: a quarter of the requests of the default page size.
  • Cache what rarely changes, such as sites, gates, booking types and forms.
  • Spread batch jobs out. A nightly import of 2,000 people at full speed would use up a Pro organisation's allowance for over three minutes; pace it with RateLimit-Remaining instead.
  • Keep test traffic in test mode. Test and live limits are separate, so load tests never slow down production.

On this page