Skip to content

Pagination

The Shoehorn API uses three pagination shapes across different endpoint families. This page documents each shape, the endpoints that use it, and how to page through results.

EndpointShapeQuery ParametersResponse Metadata Key
GET /entitiesA - Cursorlimit, cursorpage
GET /searchA - Cursorlimit, cursorpage
GET /entities/{id}/deploymentsA - Cursorlimit, cursorpagination
GET /entities/{id}/incidentsA - Cursorlimit, cursorpagination
GET /entities/zombiesA - Cursorlimit, cursorpage
GET /forge/moldsA - Cursorlimit, cursorpagination
GET /forge/runsA - Cursorlimit, cursorpagination
GET /repositoriesB - Page-basedpage, page_sizepagination
GET /usersC - Identity Providerfirst, maxpage
GET /admin/usersC - Identity Providerpage, pageSizepage
GET /governance/actionsLimit/Offsetlimit, offsettotal (top-level)
GET /teamsUnpaginated

Used by entity, search, forge, and several entity-detail endpoints. The cursor is an opaque string returned in the response; pass it back to fetch the next page.

ParameterTypeDefaultRangeDescription
limitinteger201 — 100Items per page
cursorstring(none)Opaque cursor from a previous response

Endpoints in this group return the pagination metadata under either a page or pagination key (see the quick reference for which key each endpoint uses). The field names inside differ slightly depending on the endpoint family.

Entities, Search, Zombies — metadata key: page

{
"entities": [ ... ],
"page": {
"limit": 20,
"total": 150,
"nextCursor": "20",
"prevCursor": null
}
}
FieldTypeDescription
limitintegerRequested page size
totalintegerTotal matching items
nextCursorstring or nullCursor for the next page (null on the last page)
prevCursorstring or nullCursor for the previous page (null on the first page)

Search adds total and took at the top level alongside the page object:

{
"results": [ ... ],
"sections": { ... },
"facets": { ... },
"page": {
"limit": 20,
"offset": 0,
"nextCursor": "20"
},
"total": 150,
"took": "12ms"
}

Deployments, Incidents — metadata key: pagination

{
"deployments": [ ... ],
"pagination": {
"total": 100,
"limit": 20,
"offset": 0,
"nextCursor": "20",
"prevCursor": null
}
}

Forge Molds, Forge Runs — metadata key: pagination (snake_case fields)

{
"runs": [ ... ],
"pagination": {
"total_count": 100,
"next_cursor": "20",
"has_more": false
}
}
FieldTypeDescription
total_countintegerTotal matching items
next_cursorstringCursor for the next page (empty string when done)
has_morebooleanWhether more pages exist
Terminal window
# First page
curl -H "Authorization: Bearer shp_svc_xxx" \
"https://shoehorn.example.com/api/v1/entities?limit=20"
# Next page (use nextCursor from previous response)
curl -H "Authorization: Bearer shp_svc_xxx" \
"https://shoehorn.example.com/api/v1/entities?limit=20&cursor=20"
EndpointExtra Filters
GET /entitiestype, lifecycle, owner, team, tags, search, source, manifestType, hasRelations
GET /searchq (required), types, tags, owner, lifecycle
GET /entities/{id}/deployments
GET /entities/{id}/incidentsincludeResolved
GET /entities/zombiesinactiveDays, minAge, lifecycle, type (limit max 200)
GET /forge/moldsvisibility, category
GET /forge/runsmold_slug, status

Used by the repositories endpoint. Uses classic numbered pages with complete metadata for building page controls in the UI.

ParameterTypeDefaultRangeDescription
pageinteger11+Page number (1-based)
page_sizeinteger201 — 100Items per page
{
"repositories": [ ... ],
"pagination": {
"page": 1,
"pageSize": 20,
"totalPages": 8,
"totalItems": 150,
"hasNext": true,
"hasPrevious": false
}
}
FieldTypeDescription
pageintegerCurrent page number (1-based)
pageSizeintegerActual page size
totalPagesintegerTotal number of pages
totalItemsintegerTotal matching items
hasNextbooleanWhether a next page exists
hasPreviousbooleanWhether a previous page exists
Terminal window
# First page
curl -H "Authorization: Bearer shp_svc_xxx" \
"https://shoehorn.example.com/api/v1/repositories?page=1&page_size=20"
# Page 3
curl -H "Authorization: Bearer shp_svc_xxx" \
"https://shoehorn.example.com/api/v1/repositories?page=3&page_size=20"
EndpointExtra Filters
GET /repositoriessearch, provider, owner, language, topic, team, sort, order, show_forks, show_archived, show_private

Used by user directory endpoints. The parameter names (first/max) mirror the conventions of upstream identity providers (Zitadel, Okta).

GET /users (public user directory):

ParameterTypeDefaultRangeDescription
firstinteger00+Offset (number of items to skip)
maxinteger1001 — 200Items per page
searchstring(none)Search filter

GET /admin/users (admin user list):

ParameterTypeDefaultRangeDescription
pageinteger00+Page number (0-based)
pageSizeinteger1001 — 200Items per page
searchstring(none)Search filter
{
"items": [ ... ],
"page": {
"first": 0,
"max": 100,
"count": 50,
"hasMore": true
},
"total": 250,
"provider": "zitadel"
}
FieldTypeDescription
page.firstintegerOffset used in this request
page.maxintegerPage size used in this request
page.countintegerNumber of items returned in this page
page.hasMorebooleanWhether more items exist after this page
totalintegerTotal users across all pages
providerstringIdentity provider name (e.g., zitadel, okta)
Terminal window
# First page
curl -H "Authorization: Bearer shp_svc_xxx" \
"https://shoehorn.example.com/api/v1/users?first=0&max=50"
# Next page
curl -H "Authorization: Bearer shp_svc_xxx" \
"https://shoehorn.example.com/api/v1/users?first=50&max=50"

Some endpoints use simple limit/offset parameters with a total count at the top level (no nested pagination object).

ParameterTypeDefaultRangeDescription
limitinteger501 — 200Items per page
offsetinteger00+Number of items to skip
{
"actions": [ ... ],
"total": 150,
"summary": {
"open": 45,
"in_progress": 20,
"overdue": 5
}
}
EndpointExtra Filters
GET /governance/actionsstatus, priority, entity_id, source_type, overdue

A few list endpoints return all items without pagination:

EndpointNotes
GET /teamsReturns all teams (cached 3 min per tenant)

  • Default limit: 20 for most endpoints, 50 for governance actions and zombies.
  • Maximum limit: 100 for most endpoints, 200 for governance actions, zombies, and user endpoints.
  • Cursor values are opaque: Treat cursor strings as opaque tokens. Do not parse, construct, or assume any internal format. Always use the value returned in the response.
  • Empty pages: When no results match, endpoints return an empty array with the pagination metadata reflecting zero total items.
  • Combining filters with pagination: Filters are applied before pagination. Changing a filter resets the result set, so start from the first page (no cursor / page 1).