Skip to main content
Routal

Rate limits

Routal's request quota, what happens when you exceed it, and recommended client patterns.

The Routal API rate-limits requests per authenticated credential (API key or user session). One credential, one budget — sharing a key across many services means they all draw from the same pool.

Current limit

2,000 requests per minute per credential, applied as a rolling 60-second window. This is the platform-wide default that covers the /v2/* and /v3/* REST surface documented here.

A handful of compute-heavy operations have lower per-route caps on top — geocoding being the most common one. When you cross one of those, you also get 429; the patterns below cover both cases.

What happens when you exceed the limit

You receive a 429 Too Many Requests response. A few things to know:

  • There is no Retry-After header today.
  • There is no highway.* message_id on rate-limit errors. The body for a 429 won't match the standard error envelope, so don't try to branch on message_id for this case.

If you need to differentiate a rate-limit 429 from any other 4xx, branch on the status code alone.

Until Retry-After is exposed, back off using exponential delays:

async function callWithBackoff(fn, attempt = 0) {
  try {
    return await fn();
  } catch (err) {
    if (err.status !== 429 || attempt >= 5) throw err;
    const baseMs = 1000 * 2 ** attempt;        // 1s, 2s, 4s, 8s, 16s
    const jitterMs = Math.random() * 500;
    await new Promise((r) => setTimeout(r, baseMs + jitterMs));
    return callWithBackoff(fn, attempt + 1);
  }
}

Staying under the limit

A few patterns keep you under the quota without trying:

  • Use bulk endpoints where they exist. POST /v2/stops accepts an array — one call for 100 stops is much cheaper than 100 calls of one. Same for POST /v3/vehicles.
  • Use pagination, not full scans. Most read endpoints accept ?limit=&offset= (see Pagination). Raise limit to the endpoint's documented max for sync jobs.
  • Don't poll for state changes. If you're calling GET /v2/plan/{id} in a loop waiting for status to change, replace it with a webhook subscription.
  • Cache reference data. Vehicles, projects, custom-field definitions and other rarely-changing entities can live in your cache for the duration of a planning cycle.

Concurrency

The per-minute quota is independent of concurrency. You can fire multiple requests in parallel as long as the total in the rolling window stays under 2,000. Beyond ~10 concurrent connections we typically don't see meaningful throughput gains — the bottleneck shifts to your own infrastructure.

Need more?

If your integration consistently hits the cap on real workloads, email developers@routal.com. Per-credential limits can be raised when the use case warrants it; bulk operations are usually cheaper for both of us than tight polling loops.