# Rate limits and fair use

> How many requests a key may send, what a 429 means, which requests are refused early and how to back off.

Source: https://fx-port.com/docs/rate-limits

FX-Port limits how fast a key can call the API, so that one runaway loop or one abusive client cannot slow everybody else down. The limits are far above what an interactive application needs. This page says what they are, what you get when you reach one, which requests are refused before they cost anything, and how to stay comfortably below them.

## Limits per key

Every API key has a counter per minute (the minute of the clock, not a sliding window) for each of three groups of endpoints. Sandbox keys have the same counters as live keys.

| Group | Limit per minute | Endpoints |
|---|---|---|
| **Search and pricing** | 6,000 | Flight searches (`get_flights`, `flexible_search`), `price_flight`, hotel search, rates and hotel content (`search_advanced`, `get_rates`, `get_hotel_info`, `hotels/search`, `hotels/details`) |
| **Booking** | 1,200 | Everything that creates, pays or cancels: `book_flight`, `pay_order`, `cancel_order`, `confirm_cancel`, hotel `pre_book`, `book_hotel`, `cancel_hotel`, eSIM orders and topups |
| **Everything else** | 6,000 | Reading a booking (`get_booking`, `get_ticket`, ACD documents, `GET /bookings/{id}`, hotel vouchers), eSIM catalogue, usage, instructions and bookings, balances, history, suppliers, location and name lookups |

The counter belongs to the **key**, not to the agency or the IP address: two keys of one agency count separately. A request that is refused for another reason (a malformed body, for example) still counts, so a client that loops on a bad request burns its own budget.

### Heavy endpoints have a second, tighter protection

A search costs a supplier real work, so searches and hotel name searches are also limited by how many run **at the same time** and by short bursts. These ceilings sit well above what a user-driven application produces, and they protect the suppliers (and your other calls) from a loop that fires thousands of searches at once.

| Kind of call | Per minute | Burst | At the same time |
|---|---|---|---|
| Flight search (`flexible_search` counts twice) | 300 | 60 | 12 |
| Hotel search and hotel content (`search_advanced`, `hotels/search`, `hotels/details`) | 180 | 40 | 8 |
| Hotel name search (a call counts three times) | 120 | 20 | 3 |
| Price, rates and hotel info (`price_flight`, `get_rates`, `get_hotel_info`) | 3,000 | 450 | 48 |
| Booking calls | 600 | 100 | 20 |
| Reading a booking, voucher or ticket | 1,200 | 200 | 16 |
| ACD passport upload | 30 | 10 | 3 |

- **Sandbox keys** get a quarter of every number in this table.
- All the keys of an agency together get three times the numbers of one key.
- A key can keep **4 streams** ([`stream=true`](https://fx-port.com/docs/streaming.md)) open at the same time.
- A call that has to wait for a free slot waits up to two seconds inside the API before it is refused with `concurrency_limit_exceeded`.


> [!NOTE]
> At the release of 2026-11-01 these ceilings run in observation mode: they are counted but not enforced, so nothing is refused because of them yet. We announce in the [changelog](https://fx-port.com/docs/changelog.md) before they are enforced. Whatever the numbers, the `ratelimit-*` headers of every response show the limit that applies to your request: treat them as the authority, and build your client to back off on a `429`.


## When you reach a limit

The API answers `429` with an error code that says which limit, and tells you when to come back:

| Code | Meaning |
|---|---|
| `rate_limit_exceeded` | You used the per-minute budget of the group. |
| `concurrency_limit_exceeded` | Too many calls of this kind were running at the same time. |
| `auth_rate_limited` | Too many requests with a missing or invalid key came from your IP address. Calls with a valid key are never affected. |

A `503` with `service_overloaded` or `supplier_busy` means the service or a supplier is shedding load for a moment: the answer is the same, wait and retry.

~~~http
HTTP/1.1 429 Too Many Requests
Retry-After: 17
ratelimit-limit: 6000
ratelimit-remaining: 0
ratelimit-reset: Sun, 01 Nov 2026 15:20:00 GMT
ratelimit-policy: 6000;w=60
x-fx-limit-class: flight_search
~~~

~~~json
{
  "environment": "live",
  "success": false,
  "errors": [
    {
      "code": "rate_limit_exceeded",
      "type": "rate_limit_error",
      "title": "Rate limit exceeded",
      "message": "Too many requests hit the API too quickly. Retry after the number of seconds in the Retry-After header.",
      "field": null,
      "details": { "retry_after": 17 },
      "documentation_url": "https://fx-port.com/docs/errors#rate_limit_exceeded"
    }
  ],
  "meta": { "request_id": "req_8f3a9c1d2e4b", "status": 429 },
  "request_id": "req_8f3a9c1d2e4b"
}
~~~

| Header | Meaning |
|---|---|
| `Retry-After` | Seconds to wait before the next call. |
| `ratelimit-limit` | The limit of the group the request belongs to. |
| `ratelimit-remaining` | Calls left in the current minute. |
| `ratelimit-reset` | When the counter restarts, as an HTTP date. |
| `ratelimit-policy` | The policy that applies, as `<limit>;w=<seconds>` (for example `6000;w=60`). |
| `x-fx-limit-class` | The kind of call the request was counted as: `flight_search`, `hotel_search`, `hotel_name`, `price`, `booking`, `read`, `suggest` ... |

The `ratelimit-*` headers are on every response of a key, not only on a `429`, so you can slow down before you are refused. Wait for `Retry-After`, then retry with exponential backoff and jitter (see [Errors](https://fx-port.com/docs/errors.md#retrying-safely)).

## Refused before they cost anything

Some requests never reach a supplier, so they cost neither you nor us anything:

- **Malformed input.** A wrong airport code, an impossible or out-of-range date, an absurd passenger count, an unknown enum value or a body that is not a JSON object is answered with `400` and a stable code (see [Errors](https://fx-port.com/docs/errors.md)) before any supplier is called, and it does not count as a supplier failure.
- **Oversized bodies.** A search or price request may be up to 64 KB, any other JSON request up to 256 KB, an ACD upload up to 26 MB (10 MiB per file). Larger bodies get `413 request_too_large` before they are read.
- **A key that is not a key.** A key that does not have the shape of an FX-Port key (`fxp_live_` or `fxp_test_` followed by 16 to 64 letters and digits, 80 characters at most) is refused at once with `401 invalid_api_key_format`, without a lookup.
- **Repeated bad authentication.** Requests with an unknown or malformed key are limited per IP address (30 a minute, a burst of 20). After repeated failures that address is blocked for a minute, then for longer if it continues, up to an hour. Requests with a **valid key** are never affected, even from the same address: an office behind one address is safe.

## The typeaheads

The airport and destination lookups need no key, so they are limited per IP address instead (1,200 a minute, a burst of 300). They answer `Cache-Control: public, max-age=60`: let your browser or your CDN cache them, and debounce your input.

## Stay comfortably below the limits

| Do | Why |
|---|---|
| **Cache what does not change**: airports, [suppliers](https://fx-port.com/docs/suppliers.md), hotel content, eSIM packages (a few MB per country list: cache them for an hour) | They are the biggest part of most integrations' traffic. |
| **Reuse a search** for a few seconds when several users ask for the same route and dates | Offers do not change from one second to the next, and every search costs a supplier call. The API also merges identical searches that run at the same moment, but your own cache is faster. |
| **Search in parallel with a small cap**: five at a time is plenty; do not fan out one search per day of a month | Parallel searches multiply supplier load. For a date range use [flexible search](https://fx-port.com/docs/flights/flexible-search.md). |
| **Use streaming** for a search page | The first offers arrive in about two seconds instead of after the slowest supplier ([Streaming](https://fx-port.com/docs/streaming.md)). |
| **Debounce typeahead** (airports, hotel locations) at about 300 ms and cancel the previous request | The lookups are limited per IP address too. |
| **Poll a booking every 2 seconds at most**, and stop when it is `confirmed` | Hotel bookings confirm in 4 to 14 seconds. |
| **Ask for gzip** (`Accept-Encoding: gzip`) | Flight searches are 0.7 to 14 MB of JSON; compression makes them 5 to 10 times smaller. |
| **Back off on every `429` and `5xx`** | A client that retries immediately makes the situation worse for itself. |
| **Use mini hotel data for lists** | A result list does not need the full hotel document: see [Hotel data](https://fx-port.com/docs/hotels/hotel-data.md). |

If you plan a load test or a bulk job, tell us at [dev@fx-port.com](mailto:dev@fx-port.com) first. Sandbox keys have a quarter of the heavy-endpoint ceilings, and the suppliers' test systems are small.

## Related limits

| Limit | Value |
|---|---|
| Hotel stay | 1 to 30 nights, check-in from yesterday up to 400 days ahead |
| Flight dates | From yesterday up to 365 days ahead |
| Passengers | 9 seated passengers in total, lap infants not more than adults, ages 0 to 17 for children |
| Hotel ids in one content request | 200 |
| Hotel rooms in one search | 9 |
| ACD upload | 10 MiB per file, 25 MiB per request |

The limits of each endpoint are in its page and in the [API reference](https://fx-port.com/docs/api.md).
