client_id on a sliding window. There are
two windows; whichever has the tighter remaining budget wins.
Default budgets
| Window | Budget per client_id |
|---|---|
| Per minute | 600 |
| Per day | 10,000 |
/v1/* endpoint. Public endpoints
(/healthz, OAuth, OpenAPI) are not rate-limited per-partner — they have
infrastructure-level protection instead.
Need higher limits? Premium-tier partners can negotiate. Talk to your DSS
account contact.
Headers we return
Every authenticated response (success,304, or 429) carries:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The window’s budget (e.g. 600 for per-minute). |
X-RateLimit-Remaining | How many requests you have left in this window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
Retry-After | Only on 429. Seconds to wait before retrying. |
The 429 response
Staying under budget
The default budget is generous for any reasonable integration. Here’s how to not waste it.1. Use webhooks instead of polling
Webhooks cost zero rate-limit budget. A well-integrated partner sets up webhooks on day one and only calls the API in response to events (or when the user actively opens a page that needs fresh data). See Webhooks overview.2. Use conditional GETs
Every resource endpoint returnsETag and Last-Modified and supports
If-None-Match / If-Modified-Since. A 304 Not Modified response counts
as one request against your budget but returns no body — much cheaper
than the alternative of “always full payload.”
3. Don’t tight-loop on 429
A retry loop that ignoresRetry-After will keep hitting 429 and never
recover. Always respect the header.
4. Batch background work to natural intervals
If you’re refreshing N users’ records every M minutes, schedule the batches so they don’t pile up in the same second. Spreading 1,000 users evenly across a minute is a much smaller blast radius than 1,000 requests in the same second.Recommended polling intervals
If you must poll (no webhooks available on your side):| Endpoint | Recommended max polling interval |
|---|---|
GET /v1/me | 1 hour |
GET /v1/me/sea-time | 15 minutes |
GET /v1/me/sea-time/recent | 1 hour |
GET /v1/me/vessels | 1 hour |
If-None-Match, the realistic cost of these is small —
sea-time records change a few times per month for the average crew member.
What happens on persistent overuse
If yourclient_id consistently saturates its budget we’ll get in touch
before anything changes. We may suggest pushing more of your reads onto
webhooks + conditional GETs, or talk through a higher-tier plan. We don’t
hard-suspend partners for rate-limit reasons without a conversation first.
