Rate limits
Budget you can see, not a wall you hit. Every response carries
X-RateLimit-RemainingandX-RateLimit-Reset. Four independent buckets (reads, writes, bookings-feed, analytics) mean a batch inventory push does not steal budget from your booking-feed consumer. A higher budget is a free config change, not a re-integration — see the SLA for the default numbers and Why Quendoo Connect for how this compares against the hidden fixed limits every other vendor ships.
Every call to /v1/qc/* (and to the outbound webhook fanout) is
budgeted. Budgets are per (client_id, endpoint bucket, minute)
— shared across your workers, not per host, not per token. The default
numbers live on the SLA page;
this page covers behaviour: what the headers mean, what a 429 means,
what does and does not count.
The headers on every response
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
X-RateLimit-Reset: 1735689600
X-RateLimit-Bucket: reads
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
The per-minute budget for THIS bucket at your tier. Never changes mid-window. |
X-RateLimit-Remaining |
Budget left this minute. Decrements with every call the bucket accepted. |
X-RateLimit-Reset |
Unix seconds at which X-RateLimit-Remaining resets to Limit. Fires on wall-clock minute boundaries, not on your first call. |
X-RateLimit-Bucket |
Which bucket THIS call landed in — reads / config-writes / inventory-writes / webhook-fanout. |
Read every response for these — the safe pacing rule is "if Remaining hits a low watermark, defer non-urgent calls until Reset".
429 — you spent the budget
HTTP/1.1 429 Too Many Requests
Retry-After: 27
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735689600
{"status":"error","message":"errors.qc.rate_limit_exceeded"}
Wait Retry-After seconds — never a naked retry-now loop that
just eats latency. Retry-After and X-RateLimit-Reset agree; the
former is a convenience, the latter is the authoritative wall-clock.
429 is a bucket miss, NOT an availability miss
A 429 does not count as downtime under the SLA. It's expected behaviour under a valid tier. If you keep hitting it, the answer is either back off or upgrade — never open an incident on us.
What counts
- Every request the API answered, including 4xx. A 422 validation error you triggered spent one call from your bucket before it errored.
- Retries after a 5xx. If the platform errored, we still count the retry — protect yourself with an exponential backoff.
- Requests inside an idempotent replay window. A retry that cache-replayed a previous response STILL counted a call — the cheap replay path uses less server work but the same budget slot.
What does NOT count
- Failed authentication. A 401 (missing/expired/invalid token) never touches your bucket — you're not signed in yet, there is no bucket to charge. Same for 403 scope refusals.
- Requests we short-circuited before authorization — TLS handshake failures, HTTP protocol errors (400 on malformed request line), pre-auth reject.
- OPTIONS preflights. CORS preflights are free.
- Health endpoints under
/v1/qc/health/*. The status page itself needs to poll them; leaving them budgetless keeps your own dashboards from interfering with your production budget.
The four buckets
| Bucket | Endpoints |
|---|---|
reads |
GET /properties, /room_types, /rate_plans, /availability, /rates, /bookings?revisions=…, and every configuration surface GET (extras, promos, policies, gallery, reviews, content pages). |
config-writes |
Every POST / PATCH / DELETE on the configuration surfaces — extras, promotions, policies, content, gallery, booking buttons, room types, rate plans, guest feedback. |
inventory-writes |
POST /v1/qc/properties/{ext}/inventory/* — availability, rates, restrictions. High-frequency; separate bucket by design so a burst on inventory doesn't starve config writes. |
webhook-fanout |
Outbound only — how many webhook deliveries we'll attempt on your behalf per day. |
Why "per minute" and not "per second"
A per-minute window smooths bursts a per-second window couldn't
tolerate — you can flush a 60-row inventory push in ten seconds and
sit idle for the other fifty. The trade-off is that a bad minute
looks worse in a per-minute view; expect the Remaining counter
to slide fast when a batch runs, then flatline until reset.
Pacing patterns that work
Steady state — mind the low watermark
Read X-RateLimit-Remaining off the last response; if it's below
say 10% of Limit, defer non-urgent calls (write-behind updates,
speculative reads) until Reset fires. Cost: a shorter tail on
your median latency, and no 429s in the log.
Burst — align on the reset
If you're pushing a big batch (multi-hundred-property nightly sync),
start the batch at a wall-clock second aligned with our reset —
X-RateLimit-Reset from any prior call gives you the boundary. You
get a full 60 s to burn the whole minute's budget without
throttling.
Retry — exponential + jitter
On 429 (or 5xx) use Retry-After if present, else exponential
backoff starting at 500 ms with ±20% jitter. Do NOT sleep exactly
Retry-After in a fleet — every worker unblocks at the same
instant, hammering the reset boundary. Add jitter.
What happens on a workflow bucket miss
The webhook-fanout bucket applies to outbound deliveries. When you exceed it we do NOT drop events. We queue them; the retry ladder from the SLA still runs. Sustained bucket saturation degrades your webhook latency SLA — which you own — not your delivery guarantee, which we own.
The daily fanout budget resets at 00:00 UTC.
Cross-cutting reads
- If you're integrating a new endpoint and unsure which bucket it
falls in, hit it and read
X-RateLimit-Bucket. Cheaper than guessing. - The SLA carries the default per-minute and per-day budgets.
- Idempotent retries and rate limits interact — see Idempotency.
What we don't do
- No token-bucket credit rollover. A minute you didn't spend is not saved for the next minute — the counter resets, it does not accumulate.
- No pre-emption — a
POSTthat would cross the limit is rejected outright with 429; there's no half-write. - No per-property limits below the account level — your budget is one pool across every property you can see. If one property is drowning your budget, you pace on your side.