Skip to content
novilDevelopersOpen Novil
Getting startedRate limits

Rate limits

Each API key may make 600 requests per minute. The limit is per key, not per company, so two integrating systems with their own keys do not share a budget. GET /v1/me reports the limit that applies to the key you are using:

"rateLimit": { "limit": 600, "window": "1m" }

Requests beyond the limit are rejected with 429 Too Many Requests and the code rate_limited. The response carries three headers:

Header Meaning
Retry-After Seconds to wait before retrying.
X-RateLimit-Limit The key’s limit for the window (600).
X-RateLimit-Remaining Requests left in the current window.
429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 12
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
{
"type": "about:blank",
"title": "Too many requests",
"status": 429,
"code": "rate_limited",
"detail": "More than 600 requests in a minute. Retry after 12 seconds."
}

Wait for Retry-After seconds, then retry the same request. Nothing was processed, so there is no risk of a duplicate.

A dispatch integration almost never needs the full budget. The patterns that do run into it are tight polling loops and per-route re-syncs.

  • Push routes as they are dispatched, one request per load. Re-pushing a load that has not changed is harmless (it is idempotent) but wasteful.
  • Poll lists, not individual routes. GET /v1/routes?status=planned returns up to 100 routes per request. Polling each route with GET /v1/routes/{id} every few seconds is the fastest way to hit the limit.
  • Use a from/to window on GET /v1/routes to fetch only routes scheduled in the range you care about.
  • Plans change on the order of minutes, not seconds. Polling once every few minutes is enough to see a route move from draft to planned to active.
  • Back off on 429. Honour Retry-After; if you retry with a fixed short delay you will simply be rejected again.

Webhooks for route events are planned and will remove most of the need to poll.