Skip to main content
Routal

Pagination

How list endpoints page their results, with 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
textstring—noSearch string
sortstring—noSort columns
offsetnumber0noNumber of plans to skip
limitnumber20noNumber of plans to fetch
deletedbooleanfalsenowith deleted plans
project_idstring—noPlan project ID
statusstring—noPlan 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
textstring—noSearch string
sortstring—noSort columns
offsetnumber0noNumber of vehicles to skip
limitnumber20noNumber of vehicles to fetch
deletedbooleanfalsenowith deleted vehicles
enabledbooleanfalsenoRetrieve only enabled vehicles
project_idstring—yesVehicle 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_idstring—yes(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 with a stable sort.
  • Server-Sent Events / streaming list: not available.