- Developers
- Rate limits
Rate limits
The Ferry API throttles call frequency based on the origin IP address of the partner system to safeguard platform stability. This page documents the system-wide global limit, specific tightened rules applied during ticket issuance confirmation, and other operational boundaries your integration must handle.
Global limits
Section titled “Global limits”Ferry enforces a single default rate-limiting policy across all public API endpoints: 50 requests per 100 milliseconds, counted by origin IP address.
The evaluation window is measured in milliseconds (100 ms equals 1/10th of a second). Consequently, 50 requests per 100 ms allows sustained throughput of up to approximately 500 requests per second.
This ceiling is designed to prevent aggressive traffic bursts or denial-of-service spikes. A properly engineered booking integration operating under normal production conditions rarely approaches this threshold.
The diagram below shows the path of a call through the rate-limiting layers and the outcome of each branch:
flowchart TB CALL["A call from your system"] IP{"Layer 1 — counted per IP address<br/>50 calls per 100 milliseconds"} OVER{"Layer 2 — ticket issuance confirmation step?<br/>5 calls per 60 seconds"} LOCK{"Layer 3 — ticket issuance lock per booking<br/>valid for 600 seconds"} SOFT{"Layer 4 — ticket issuance soft limit<br/>5 times per 60 seconds"} OK["Ferry processes normally"] E429["You get 429<br/>call again with growing backoff"] LOCKED["You get business error 120100<br/>wait at least 60 seconds then call again"] SOFTED["You get a business error<br/>that is not 429"]
CALL --> IP IP -->|"over the IP cap"| E429 IP -->|"within the cap"| OVER OVER -->|"not the confirmation step"| OK OVER -->|"confirmation step, over 5 calls in 60 seconds"| E429 OVER -->|"confirmation step, within the cap"| LOCK LOCK -->|"the booking already has another issuance in flight"| LOCKED LOCK -->|"the booking is free"| SOFT SOFT -->|"over 5 times in 60 seconds"| SOFTED SOFT -->|"within the cap"| OKBehavior when you exceed the cap
Section titled “Behavior when you exceed the cap”When traffic exceeds the allowed threshold, the system returns HTTP 429 (Too Many Requests) accompanied by the timestamp and requested path.
Key operational realities:
- No customized error message: The response body contains the standard framework message. Your error parser should not expect localized strings in this payload.
- Ferry does not log 429s as server errors: The platform error filter records logs starting only at status 500. Ferry will not automatically detect that your client is being throttled; monitoring 429 response rates is strictly the partner’s responsibility.
Recommended retry strategy: when receiving HTTP 429, implement randomized exponential backoff (starting at 1 second, doubling per attempt, capped at 30 seconds, up to 5 attempts).
Per-group exceptions
Section titled “Per-group exceptions”Most endpoints operate under the global policy. Three specific endpoint categories carry distinct rules:
| Call group | Limit applied | Counting basis | Operational reality |
|---|---|---|---|
| System-wide (default) | 50 calls / 100 ms | Origin IP | Applies to lookups, order creation, general management |
| Ticket issuance confirmation | 5 calls / 60 seconds | Origin IP | Tightened to protect upstream ferry operator gateways |
| Public booking search | 60 calls / 60 seconds | Origin IP | Intended for public end-customer search portals |
| Real-time SSE event stream | Exempt from limits | Not applicable | Reserved for web sessions, not available to API keys |
Two mechanisms sharing the same 5-per-60-seconds number
Section titled “Two mechanisms sharing the same 5-per-60-seconds number”The ticket issuance confirmation step features two distinct protective mechanisms sharing the figure of 5 attempts per 60 seconds:
| Mechanism | Counting basis | Response when exceeded |
|---|---|---|
| Network-layer rate limit | Your client IP address | HTTP 429 with standard message |
| Business-layer soft limit | Specific booking code | Business error (6-digit code), not 429 |
The per-booking soft limit allows at most 5 confirmation attempts within 60 seconds on the same booking. This safeguards against users repeatedly clicking confirmation buttons while an initial issuance is in progress.
Retry rules:
- On HTTP 429: Pause, apply exponential backoff, and retry.
- On Business error: Query current booking status first; blind retries simply reproduce the same business failure.
Other limits you will run into
Section titled “Other limits you will run into”Beyond time-based request throttling, partners must account for several structural technical limits:
| Limit | Ceiling value | Behavior when exceeded |
|---|---|---|
| Inbound payload size | Maximum 1 MB | Request rejected at perimeter layer |
| Page size in pagination | Maximum 50 (100 on select APIs) | Silently clamped: system returns maximum page size without throwing an error |
| Booking ticketing lock | Held for 600 seconds | Second concurrent request receives error code 120100 |
| Booking payment lock | Held for 180 seconds, independently of the issuance lock | Concurrent payment receives error code 120100 |
| Email verification codes | 5 attempts / 15 minutes per email | Sixth attempt onward is rejected |
| Webhook receiver timeout | 2,000 milliseconds | Endpoints slower than 2 seconds cause permanent event loss |
Two key takeaways:
- Silent pagination clamping: Requesting 200 records and receiving 50 is normal behavior. Do not assume data is exhausted simply because fewer items arrived; follow the pagination cursor.
- Webhook timeout is your responsibility: Your webhook receiver must respond within 2 seconds. Ingest payloads into an asynchronous queue and return HTTP 200 immediately.
What to do when you are throttled
Section titled “What to do when you are throttled”Five sequential resolution steps:
- Reduce parallel worker threads instead of adding concurrency: Limits count by IP address; increasing thread counts saturates the counter faster and extends lockouts.
- Apply randomized exponential backoff (jitter): Introduce random jitter into retry delays to prevent synchronized wave storms.
- Decouple read and write queues: Prevent background voyage lookups from exhausting the narrow 5-call/60-second limit required for ticketing confirmation.
- Log request trace IDs: Record the timestamp, trace ID, and origin IP of every 429 response to assist in reconciliation.
- Contact Ferry if using shared egress IPs: Frequent 429 errors under moderate traffic usually indicate an egress IP shared with another active tenant.
Common misconceptions
Section titled “Common misconceptions”| Misconception | Reality | Consequence |
|---|---|---|
| Quotas are assigned per API key | Limiter evaluates traffic by IP address | Two partners behind a shared NAT block each other |
| Limit is 50 calls per 100 seconds | Unit is milliseconds (~500 req/s) | Designing needlessly sluggish request queues |
| All rate limits return HTTP 429 | Per-booking soft limits return business errors | Retry handlers skip business exceptions entirely |
| Ferry monitors and alerts on 429s | Server logs do not record 429 at error level | Outages persist undetected unless monitored locally |
| When throttled, spawn more threads | More threads saturate the IP counter faster | Throttling duration increases |
| Receiving fewer records than requested is a bug | System silently clamps pages to 50–100 items | Stopping pagination prematurely, missing data |
| Delayed webhooks reflect Ferry transmission lag | 2-second timeout is measured at your receiver | Fixing the wrong side while events continue dropping |
Where to look in Scalar
Section titled “Where to look in Scalar”Rate limits are not documented inside specific groups on https://api.ferry.vn/docs, as OpenAPI specifications do not reflect dynamic throttling:
| Inspection target | Group in API reference |
|---|---|
| Ticket issuance confirmation (tightened to 5 calls / 60 s) | Bookings |
| Endpoints handling large payloads (subject to 1 MB cap) | Bookings, Transactions |
| Read-only endpoints using global policy | Voyages, Organizations, Customers |
Read next
Section titled “Read next”| Page | When to read |
|---|---|
| Errors and how to handle them | Learn how to parse HTTP 429 and business error codes |
| Conventions | Understand cursor pagination rules and silent clamping |
| Booking flow | Design booking workflows around throttled operations |
| Ticketing | Understand ticketing locks and soft limits during issuance |
| Webhooks | Build event receivers that respond within 2,000 ms |
| Go live | Add rate-limit monitoring to your pre-launch checklist |
Open questions
Section titled “Open questions”- SLA on custom partner throughput: The default 50 calls/100 ms policy is a technical baseline rather than a contracted Service Level Agreement (SLA). Ferry may adjust thresholds based on aggregate cluster load.
- Presence of Retry-After headers: It remains unconfirmed whether all 429 responses reliably return a
Retry-Afterheader; client retry loops must manage backoff intervals independently.