Skip to main content
Routal

Pagination

How list endpoints page their results — offset, limit, and the response envelope.

Every list endpoint in the Routal API returns the same paginated envelope. This page documents the shape and the per-endpoint defaults.

Defaults and parameter lists below are pulled live from the OpenAPI spec at build time. If the backend changes a default, this page updates on the next deploy.

Response envelope

{
  "total": 1000,
  "limit": 50,
  "offset": 0,
  "pages": 20,
  "page": 1,
  "docs": [ /* items */ ]
}
FieldTypeDescription
totalintegerTotal items matching the filter, ignoring limit / offset.
limitintegerEcho of the requested limit (or default).
offsetintegerEcho of the requested offset (or default).
pagesintegerTotal pages at the current limit (ceil(total / limit)).
pageintegerCurrent page, 1-indexed (floor(offset / limit) + 1).
docsarrayThe page of items.

The shape is the same across /v2/plans, /v2/vehicles, and /v2/stops/search.

Per-endpoint defaults

Defaults are not uniform — each endpoint declares its own. The tables below come straight from the spec.

GET /v2/plans

NameTypeDefaultRequiredDescription
textstringnoSearch string
sortstringnoSort columns
offsetnumber0noNumber of plans to skip
limitnumber20noNumber of plans to fetch
deletedbooleanfalsenowith deleted plans
project_idstringnoPlan project ID
statusstringnoPlan status

sort follows the format field:asc|desc (e.g. created_at:desc).

curl -G 'https://api.routal.com/v2/plans' \
  --data-urlencode 'private_key=YOUR_KEY' \
  --data-urlencode 'limit=50'

GET /v2/vehicles

NameTypeDefaultRequiredDescription
textstringnoSearch string
sortstringnoSort columns
offsetnumber0noNumber of vehicles to skip
limitnumber20noNumber of vehicles to fetch
deletedbooleanfalsenowith deleted vehicles
enabledbooleanfalsenoRetrieve only enabled vehicles
project_idstringyesVehicle project ID

POST /v2/stops/search

Pagination here lives in the request body, not the query string:

FieldTypeRequiredDescription
limitintegernoLimit of results
offsetintegernoOffset of results
sort_bystringnoSort by field
sort_directionstring (enum)noSort order
predicatesobject[]noPredicates to search

Query string carries only project_id (and your private_key):

NameTypeDefaultRequiredDescription
project_idstringno(no description in spec)

Iterating all items

async function* iteratePlans(privateKey) {
  const limit = 50;
  let offset = 0;
  while (true) {
    const url = new URL('https://api.routal.com/v2/plans');
    url.searchParams.set('private_key', privateKey);
    url.searchParams.set('limit', String(limit));
    url.searchParams.set('offset', String(offset));
    const res = await fetch(url);
    if (!res.ok) throw new Error(`Failed: ${res.status}`);
    const body = await res.json();
    for (const plan of body.docs) yield plan;
    offset += limit;
    if (offset >= body.total) return;
  }
}

For stop search, swap the GET for a POST with the body shape above and read body.docs the same way.

Recommendations

  • Cache total only at the start of a job. It can shift while you paginate if other users mutate resources.
  • Pin a sort order. Without sort (or sort_by + sort_direction for stops/search) the same record can appear on two pages or be skipped if rows are inserted mid-pagination.
  • Don't deep-paginate to find one record. If you know the id or external_id, fetch it directly (GET /v2/plan/{id}) or filter through POST /v2/stops/search with a predicate.

Empty pages

When offset >= total, the response is docs: [] with total echoed accurately. Treat that as the end of the iteration.

What's not supported

  • Cursor-based pagination (?after=…) — not available. Use offset + a stable sort.
  • Server-Sent Events / streaming list — not available.