Pagination and limits
Most InfraNode endpoints return compact payloads. A few return large lists: all charging points, all energy installations or all public-transit stops of a major city quickly reach several megabytes. Those lists are paginatable so that AI agents (MCP) and GPT Actions do not hit their size limits, while bulk consumers of the REST API still get everything in a single call.
The key point: pagination is optional and only applies to large,
static lists, never to live data. The default additionally depends
on the calling channel. Both are transparent via meta.pagination
in every response.
What is paginatable, what is not
Only the large, slowly changing location/inventory lists are paginated. Live data always stays complete because it is small and current, and a partial view would make no sense there.
| Category | Examples | Paginatable |
|---|---|---|
| Large static lists | energy, charging (locations),
transit (stops), events, and OSM lists such
as education, playgrounds,
post-boxes | Yes, optional |
| Live and compact data | weather, air, traffic,
transit/departures, charging-status,
water-level, flood, power-load | No, always complete |
For stop lookup there is also ?q= (name) and
?near=lat,lon (radius) on transit, so you get a few
targeted hits instead of the whole city.
Channel-dependent default
Without an explicit limit parameter, the default page size
depends on which channel the call arrives through. This keeps the REST API
unchanged for existing bulk consumers while automatically protecting the two
channels with hard size limits.
| Channel | Default | Why |
|---|---|---|
Direct REST (/api/v1) | full (no limit) | Data pullers get everything in one call, no breaking change. |
| GPT Actions | bound (50) | Detected via the OpenAI headers, enforced server-side so the response stays under the Action size limit. |
| MCP server | bound (50) | The MCP client sets the limit itself, keeping the agent context lean. |
Good to know: the same URL can return a different number of
entries across two channels. This is intentional and readable from
meta.pagination in every response (see below), so it is never
unclear whether you have a slice or the full set.
So a shared cache never mixes up this channel-dependent response, GPT Actions
responses are not cached at the edge: the API sends
Cache-Control: no-store for GPT requests and Cloudflare
additionally excludes that traffic from the edge cache via a rule. Otherwise
the same shared cache would serve the wrong variant to the other channel
under one URL: REST would get the bound 50-entry response, GPT the full,
potentially multi-megabyte response.
Parameters
| Parameter | Meaning |
|---|---|
limit |
Entries per page. Integer from 1, hard-capped at
200 (MAX_LIMIT). A larger value returns a
200-entry page instead of an error.
|
offset |
Start position for paging (default 0). An offset beyond
the data set returns an empty page with status 200, not an
error.
|
limit=all or ?all=1 | Channel-independent full switch: forces the complete list, even over GPT Actions and MCP. The self-documenting way to deliberately fetch everything. |
meta.pagination
Every paginatable response carries a pagination block in the
meta object. It makes the slice honest and visible, there is no
silent truncation.
| Field | Content |
|---|---|
total | Total number of entries in the full set. |
returned | Number of entries included in this response. |
limit / offset | The page size and start position actually applied. |
truncated | true when a slice is returned (there are more than
returned), otherwise false. On full output,
returned == total and truncated: false.
|
Full geometry for traffic
The traffic endpoint (live) is not paginated but kept lean: the
raw roadworks polyline geometries are hidden by default because they multiply
the response size and are irrelevant to most calls. Map applications request
them explicitly:
https://infranode.dev/api/v1/cities/koeln/traffic?include=geometry
Alternatively ?full=1 has the same effect. Without this
parameter the official content (title, description, location, impact) stays
complete, only the polyline is omitted.
Examples
First 20 charging locations, starting at the 40th entry:
https://infranode.dev/api/v1/cities/berlin/charging?limit=20&offset=40
All energy installations in one call, regardless of channel:
https://infranode.dev/api/v1/cities/berlin/energy?limit=all
Find a stop by name (returns a few hits with stop_id):
https://infranode.dev/api/v1/cities/hamburg/transit?q=Alsterdorf