Money and volume

Search quota

Searching costs us money at the supplier, so your terms carry a free daily allowance and a price beyond it. The counter runs today and every authenticated response tells you where you stand; the invoice line does not exist yet. This page says exactly which calls are counted, because the rule has edges.

The allowance

Free allowanceFrom your terms; 1,000 billable searches per day unless your contract says otherwise. The number that applies to you is limits.freeSearchesPerDay in GET /v1/account, and the portal dashboard shows the same figure — with your hard cap beside it, if you have one. What neither shows is how much you have used today: that lives only in the response headers below.
Day boundaryMidnight in your account's time zone, so a partner in Singapore gets a day that ends at Singapore midnight rather than eight hours into his afternoon. Accounts with no time zone set — and any value we cannot resolve — fall back to UTC. Line your own reporting up with the same zone; if you are not sure which one is on your account, read X-Search-Quota-Reset, which names the next boundary outright.
Beyond the allowancePriced in your contract. It is metered per day but nothing is invoiced for it today — there is no search line on any invoice and no ledger entry. Ask your Vacabee contact before you budget for it, and do not build a reconciliation against a bill that is not issued.
SandboxNever counted, whatever the volume. Rate limits still apply, so a load test against the sandbox will hit 429 rate_limit_exceeded long before anything is counted — it is free, not unlimited.

What counts as a search

The intent is that a call is billable when it costs us a supplier call. The implementation is a fixed list of routes, and two edges differ from the intent — both are named below rather than left for you to discover on an invoice.

Counted — 1 searchFree
POST /v1/hotels/searchesGET /v1/hotels/searches/{id} — re-reading a search, however often
POST /v1/flights/searchesRe-sorting: sort is not a billable field, so a PATCH that only changes it is free
POST /v1/transfers/searches — one search, even though a return journey costs us one supplier request per legThe eSIM catalogue — countries, regions, plans and one plan
POST /v1/hotels/searches/{id}/load-moreBookings, orders, cancellations and every read-back
GET /v1/hotels/{hotelId}/ratesWebhook deliveries
GET /v1/flights/offers/{id} — the offer is re-priced at the airlineEverything in the sandbox
PATCH /v1/hotels/searches/{id}, when a billable field changesGET /v1/ping, /health and the account and ledger reads

A PATCH counts when it changes one of checkIn, checkOut, destination, latitude, longitude, adults, children, childrenAges, rooms or currency. Everything else — sort, limit, board, starMin, amenities, budgetMaxPerNight — is free.

Two edges worth knowing

  • A retried search is counted again. The counter sits outside the idempotency store, so a POST …/searches replayed from a stored response is counted even though no supplier was contacted. Retrying after a timeout is still the right thing to do — just do not treat it as free.
  • Some refinements reach the supplier without being counted. A PATCH always issues an upstream request, but radiusKm, nearPoi and areaHint are not on the billable list, so changing only those is free. The list is an allow list on purpose: when in doubt, you are not charged.

Quota headers

Four headers, on every response of an authenticated route once the request is past the guards. They are not on public routes such as /v1/ping, and not on a 401, 403 or 429 raised before the request reached the quota layer — their absence there is normal, not a broken proxy.

Response headers — this account's time zone is Asia/Singapore
X-Search-Quota-Limit: 1000
X-Search-Quota-Remaining: 412
X-Search-Quota-Reset: 2026-08-29T16:00:00.418Z
X-Search-Billable: true

Note the reset instant: it is the next Singapore midnight, which is 16:00Z on the previous UTC date. Code that assumes the reset always ends in 00:00:00Z, or that derives the quota day by truncating it in UTC, gets the wrong day for every account outside UTC.

HeaderMeaning
X-Search-Quota-LimitYour free allowance for the current day.
X-Search-Quota-RemainingHow much of it is left. Reaches 0 and stays there; it does not go negative.
X-Search-Quota-ResetThe next midnight in your account's time zone, as an ISO-8601 instant. Unless that zone is UTC it will not land on an even hour in UTC, and it carries milliseconds a few hundredths off the boundary — parse it as ISO-8601, not with a fixed second-precision pattern, and do not assume it ends in 00:00:00Z.
X-Search-BillableWhether this route would count. It is set before the handler runs, so it is a prediction: a call that then fails still says true but is not counted, and a replay says true and is counted. Note the name — it is X-Search-Billable, not X-Search-Quota-Billable, so a prefix filter on X-Search-Quota- drops exactly this one.

You can also just ask — GET /v1/account/quota

The headers only appear on a call you were making anyway, which is awkward for a dashboard or an alert. GET /v1/account/quota is the same live counter as a normal response: day, timezone, limit, used, remaining, reset, hardCap and rejected — the last being how many searches your hard cap turned away today.

Reading it is free and does not count against the allowance, so it is safe to poll. It needs the account:read scope. It is also the most reliable way to find out which time zone your day is keyed to, because it returns that zone by name.

Reconcile against X-Search-Quota-Remaining or the remaining field, which is the counter itself, rather than by summing X-Search-Billable. If you use the TypeScript SDK, readQuota() surfaces the remaining free searches as freeSearchesRemaining; the other three headers are on the raw response.

Running out

Nothing switches off. Exhausting the allowance moves you onto the per-search price in your contract; it never turns a live search into an error, because a traveller mid-booking is a bad place to discover a budget.

  • If you would rather be stopped than billed, ask your Vacabee contact to set a daily hard cap on your terms. It is not a self-service setting — the portal shows your terms read-only and has no field for it, and the message on the 429 that says otherwise is wrong. Once your counter is past the cap, further billable searches are refused until the reset; because the check runs against the counter as it stood before the current call, one search still gets through at the boundary.
  • That rejection arrives as 429 typed search_quota_exceeded, with a message naming your ceiling and a resetAt in the error details. Branch on the type: the per-minute limiter is a 429 too, but typed rate_limit_exceeded. There is no Retry-After on the daily one, and there is nothing useful to wait for in seconds — honour resetAt instead, or stop until the next boundary. Its message ends “You can change it in the partner portal” — as above, you cannot.
  • Watch it yourself — poll GET /v1/account/quota, or read the headers on traffic you are sending anyway. There is no notification: the search_quota.threshold webhook is in the event catalog but nothing publishes it, and no page in the portal shows your consumption. See webhooks for the full list of events that are declared and silent.
  • Cache on your side. Reading a search back is free, and so is re-sorting, so a results page that re-reads an existing search costs nothing — a fresh query, a load-more and a rate lookup are what count.

Look-to-book

Supplier rate limits are shared across all Vacabee traffic, so an extreme look-to-book ratio can degrade availability for everyone. Nothing measures that ratio today and no alert will reach you, so if you are planning a large price-comparison workload, tell us first — that conversation is the whole mechanism.
NextSettlement modesWho is merchant of record. Fixed per key, not per booking.