Pagination
How a list is walked, what each plan may read of it, and every operation that pages. Read from the OpenAPI spec and the API's own limits, so the figures here are the ones the API enforces.
- 30Operations with page + per_page
- 6Capped lists with limit
- 100Max per_page
- 10,000Highest page number
Two parameters
Every list takes the same two query parameters, declared once in the spec and shared by every operation. Anything outside the bounds is a 400 with INVALID_PARAMETER; an unknown parameter is ignored, not refused.
pageintegerPage number, from 1 to 10,000
- Min
- 1
- Max
- 10,000
- Default
- 1
per_pageintegerItems per page (max 100)
- Min
- 1
- Max
- 100
- Default
- 20
per_page tops out at 100 almost everywhere. A larger ceiling (1000) applies to the bulk lists: /v1/firearms/media.
6 operations are rankings rather than lists: they take a single limit, return the top rows and carry no pagination object: /v1/firearms/top, /v1/popular/firearms, /v1/stats/calibers/popular, /v1/stats/manufacturers/prolific, /v1/stats/feature-frequency, /v1/data/tasks.
The envelope
A paged response wraps the page in the same object every time. The sample below is built from the schema's own examples, so it cannot drift from what the API sends.
{ "success": true, "data": [ /* one page of records */ ], "pagination": { "page": 1, "per_page": 20, "limit": 20, "total": 42, "totalPages": 3 }}pagination.pageintegerrequiredThe page this response is, 1-based.
pagination.per_pageintegerrequiredPage size. Same value as
limit.pagination.limitintegerrequiredPage size. Alias of
per_page.pagination.totalintegeroptionalTotal records matching the query across every page.
pagination.totalPagesintegeroptionalHow many pages the result set spans at the current
per_page.
Page-size is reported under both per_page (matching the request parameter) and limit (retained for existing integrations). They always carry the same value. total and totalPages are omitted for Explorer and anonymous callers.
Walking a list
The loop that works on every plan drives itself from the page it just received, not from totalPages. Four rules and the code in three languages.
- 1Stop when a page comes back short. Fewer rows than
per_pagemeans it was the last page, on every plan. - 2Treat
totalandtotalPagesas optional extras. They are absent on Explorer and anonymous calls, so a loop that testspage < totalPagesstops after one page there. - 3Ask for the largest page you can use.
per_page=100is 100× fewer requests against your per-minute limit and your daily allowance, and reaches the same rows. - 4Walk once, then stay current by other means. Each page carries an
ETagand answers 304 when unchanged, and data webhooks push catalog changes so a mirror never re-walks the list. More than 10 sequential pages inside 90 seconds is refused.
cURL
# Page 1, then keep going while a page comes back fullcurl -H "X-API-Key: your_api_key" \ "https://api.gunspec.io/v1/firearms?category=rifle&per_page=100&page=1" curl -H "X-API-Key: your_api_key" \ "https://api.gunspec.io/v1/firearms?category=rifle&per_page=100&page=2"What each plan may read
Paging is where the plans differ most, and each rule below is enforced by the API rather than by convention. The figures come from the same configuration the middleware reads.
Page depth
The deepest page number a plan may request on the firearm lists. Past it the response is a 403; narrow the list with filters or upgrade.
- Explorer
- 5 pages
- Builder
- 25 pages
- Studio
- ∞
- Enterprise
- ∞
Total counts
Whether the envelope carries total and totalPages. Hidden on the free plan, so the size of the catalog cannot be read off a list.
- Explorer
- —
- Builder
- ✓
- Studio
- ✓
- Enterprise
- ✓
Sequential-page burst
10 or more consecutive pages inside 90 seconds is a 429, per key or per address.
Aimed at crawlers. A reader paging a UI never reaches it; a script that wants the catalog should use per_page at its ceiling, filters, or webhooks.
Hard ceilings
page stops at 10,000 and per_page at 100 on every plan, including Enterprise.
A value above the ceiling is a 400, not a clamp: per_page=500 is refused rather than silently served as 100.
How a refused page request reads
The plan does not page this deep into a list. Narrow the list with filters, or upgrade for deeper paging.
Too many sequential pages in a short time. Slow the walk down, or use a larger
per_page.
Sorting
Where a list can be ordered it declares a sort vocabulary and an order direction; the default order is the one the reference shows for each. Every other list has a fixed order the operation's description states.
nameweightyearcalibercreated_atfavorites
order: asc | desccreated_atupdated_atstatuspriority
order: asc | desc
Every operation that pages
30 operations take page and per_page and answer with the envelope; 6 take a single limit. Each row links to its reference page.
| Row | Endpoint | Style | Default | Max | Plan |
|---|---|---|---|---|---|
| 01 | /v1/firearmsList firearms | page + per_page | 20 | 100 | Explorer |
| 02 | /v1/firearms/searchFull-text search | page + per_page | 20 | 100 | Builder |
| 03 | /v1/firearms/mediaIndex every firearm that has imagery | page + per_page | 250 | 1000 | Explorer |
| 04 | /v1/firearms/topRank firearms by a superlative | limit | 10 | 25 | Builder |
| 05 | /v1/firearms/by-featureFilter by feature | page + per_page | 20 | 100 | Builder |
| 06 | /v1/firearms/by-actionFilter by action type | page + per_page | 20 | 100 | Explorer |
| 07 | /v1/firearms/by-materialFilter by construction material | page + per_page | 20 | 100 | Builder |
| 08 | /v1/firearms/by-designerFilter by designer | page + per_page | 20 | 100 | Builder |
| 09 | /v1/firearms/by-conflictFilter by conflict | page + per_page | 20 | 100 | Studio |
| 10 | /v1/firearms/power-ratingRank by composite power rating | page + per_page | 20 | 100 | Builder |
| 11 | /v1/firearms/timelineBrowse the catalog chronologically | page + per_page | 50 | 100 | Builder |
| 12 | /v1/popular/firearmsList the most viewed firearms | limit | 10 | 20 | Explorer |
| 13 | /v1/manufacturersList manufacturers | page + per_page | 20 | 100 | Explorer |
| 14 | /v1/manufacturers/{id}/firearmsList a manufacturer firearms | page + per_page | 20 | 100 | Explorer |
| 15 | /v1/calibersList calibers | page + per_page | 20 | 100 | Explorer |
| 16 | /v1/calibers/{id}/firearmsList firearms in a caliber | page + per_page | 20 | 100 | Explorer |
| 17 | /v1/calibers/{id}/ammunitionList ammunition for a caliber | page + per_page | 20 | 100 | Builder |
| 18 | /v1/ammunitionList ammunition loads | page + per_page | 20 | 100 | Builder |
| 19 | /v1/categories/{slug}/firearmsList firearms in a category | page + per_page | 20 | 100 | Explorer |
| 20 | /v1/stats/calibers/popularRank calibers by adoption | limit | 20 | 100 | Explorer |
| 21 | /v1/stats/manufacturers/prolificRank manufacturers by output | limit | 20 | 100 | Explorer |
| 22 | /v1/stats/feature-frequencyRank features by frequency | limit | 50 | 100 | Builder |
| 23 | /v1/data/tasksList data remediation tasks | limit | 50 | 200 | Explorer |
| 24 | /v1/data/gaps/recordsList records missing a field | page + per_page | 20 | 100 | Enterprise |
| 25 | /v1/data/confidenceGet per-record confidence scores | page + per_page | 20 | 100 | Enterprise |
| 26 | /v1/me/favoritesList your favorites | page + per_page | 20 | 100 | Explorer |
| 27 | /v1/me/reportsList your data reports | page + per_page | 20 | 100 | Explorer |
| 28 | /v1/me/supportList your support tickets | page + per_page | 20 | 100 | Explorer |
| 29 | /v1/me/webhooksList your webhook endpoints | page + per_page | 20 | 100 | Explorer |
| 30 | /v1/game-stats/versions/{version}/firearmsList firearms in a snapshot | page + per_page | 20 | 100 | Builder |
| 31 | /v1/changelogList changelog entries | page + per_page | 20 | 100 | Explorer |
| 32 | /v1/blogList blog posts | page + per_page | 20 | 100 | Explorer |
| 33 | /v1/attachmentsList attachments | page + per_page | 20 | 100 | Explorer |
| 34 | /v1/attachments/{id}/firearmsList firearms an attachment fits | page + per_page | 20 | 100 | Studio |
| 35 | /v1/vendor/offersRead back your listings | page + per_page | 20 | 100 | Enterprise |
| 36 | /v1/interfaces/{id}/firearmsList firearms exposing a standard | page + per_page | 20 | 100 | Studio |
SDKs and agents
The official SDKs expose an auto-paging iterator that applies every rule on this page: it fetches pages on demand, stops on a short page and never reads totalPages.
for await (const firearm of client.firearms.listAutoPaging({ category: 'rifle' })) { console.log(firearm.name); // pages are fetched on demand}for firearm in client.firearms.list_auto_paging({"category": "rifle"}): print(firearm["name"]) # pages are fetched on demandHanding this to a coding agent? The agent packs and llms.txt carry the same rules in a form a model can act on: page from the short-page signal, cap per_page, and read error.reason on a 403 or 429 before retrying.