Requests are metered per client IP before auth and per user after.
Two kinds of per-user quota apply: a general one across every operation, and a tighter one on each of the costliest operations, so one expensive funnel cannot consume your whole allowance. Each costly operation is metered over three windows at once, a minute, an hour and a day, and a request is rejected when any one of them is spent.
| Operation | Per minute | Per hour | Per day |
|---|---|---|---|
| every operation | 300 / 60s | – | – |
createFlightSearch |
10 / 60s | 100 / 3600s | 500 / 86400s |
createHotelSearch (city or geo) |
10 / 60s | 100 / 3600s | 500 / 86400s |
createHotelSearchScoped (a create carrying hotelId) |
30 / 60s | 300 / 3600s | 1500 / 86400s |
getHotelRates |
60 / 60s | 600 / 3600s | 3000 / 86400s |
These are the default quotas, and an account can be placed on a larger one. Your own quota is whatever RateLimit-Policy states on the response in front of you, so pace against that rather than against this table.
The minute window is there for bursts, the hour and day windows for sustained volume. Running at the full minute quota without pause spends the hour quota in ten minutes, so a long-running workflow has to pace itself against the hour and day quotas, not the minute one.
Creating a search is the costly step, so thread a searchId through results rather than re-creating: filtering, sorting and paging a search you already hold spends only the general quota.
getHotelRates is metered because it is reached once per hotel rather than once per search, and it is looser than the creates because reading a hotel’s rates against a freshly created search usually takes more than one call while rates land.
A hotel search is split in two because the two cost different amounts. A create carrying hotelId asks one property’s providers, not a whole city’s, and it is the search getHotelRates is built for – the rates read rejects a search it can positively identify as city- or geo-scoped – so every fresh room comparison spends one: it gets 30 / 60s. The rates quota is looser (60 / 60s) because one create is usually followed by several reads while rates land, but a workflow that mints a fresh search per hotel can still hit the create quota first.
Reading the headers
RateLimit and RateLimit-Policy are lists carrying one entry per quota window that applied to the request. An operation with no quota of its own names the general quota alone. A metered operation names it plus its own three windows, except when the general quota rejects the request: that happens before the operation’s own quota is counted, so such a 429 names only user.
Each window has its own name: the minute window carries the quota name from the table above, the hour window adds -hour and the day window adds -day. The quota name is usually the operationId, but not always: a hotel create carrying hotelId is metered as createHotelSearchScoped, so match on the name the header actually carries rather than on the operation you called.
RateLimit-Policy: "user";q=300;w=60, "createFlightSearch";q=10;w=60, "createFlightSearch-hour";q=100;w=3600, "createFlightSearch-day";q=500;w=86400
RateLimit: "user";r=283;t=41, "createFlightSearch";r=8;t=41, "createFlightSearch-hour";r=97;t=2000, "createFlightSearch-day";r=480;t=80000
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 8
X-RateLimit-Reset: 41
X-RateLimit-Resource: createFlightSearchq is the quota, w the window in seconds, r what you have left, and t the seconds until that window resets.
The older X-RateLimit-* headers describe a single quota: the tightest window of the operation you called, meaning the one with the least left, and X-RateLimit-Resource names which one. On a 429 they describe the window that rejected you instead, the same one Retry-After is timed to. On an operation with no tighter quota of its own, that is user.
Open endpoints carry no quota headers, and neither does a 401: metering happens after your token is verified.
When you are limited
Quota is spent per request attempted, so a rejected or invalid request counts too, and every window of an operation is spent by the same attempt: a request the minute quota rejects also costs you an hour and a day unit. On a 429 honor Retry-After before retrying; it takes precedence over the reset in RateLimit, and it carries the latest reset among the windows you hit, so a spent day quota can mean a long wait. The X-RateLimit-Resource header on a 429 tells you which window you hit. An upstream 503 also carries a Retry-After; honor it too, it is a short transient hint, not a quota reset.