Reference
Pagination
Set a page size, follow next_cursor and stop at the last page when listing sessions or world entries.
All five list endpoints take limit and cursor. Endpoints that return a single record give back one object and skip pagination.
Query parameters
Leave out cursor on your first call. A cursor that is empty also begins at the start.
| Parameter | Type | Description |
|---|---|---|
limit |
integer | Optional positive page size. Defaults to 50. Values above 100 are capped at 100. |
cursor |
string | Optional next_cursor from the previous response to this list endpoint. Treat it as an opaque value. |
curl "https://app.saga20.com/api/public/v1/characters?limit=1" \
-H "Authorization: Bearer $SAGA20_API_KEY"
Response fields
Every list response holds a data array plus a pagination object. The API gives no total count.
| Field | Type | Description |
|---|---|---|
data |
array of objects | Records on this page. Each endpoint documents its record fields. |
pagination |
object | Whether another page is available and the cursor to request it. |
pagination.next_cursor |
string or null | Pass this value as cursor for the next page. Null on the last page. |
pagination.has_more |
boolean | True when another page is available. False on the last page. |
{
"data": [
{
"id": "a4f1b6d2-0e5c-4b7a-8c33-1d9e2f7a6b50",
"name": "Nyra Vael",
"icon": "saga20:mage",
"hidden_from_wiki": false,
"created_at": "2026-09-02T18:00:11+00:00",
"updated_at": "2026-09-25T08:04:02+00:00",
"is_player_character": true
}
],
"pagination": {
"next_cursor": "eyJvZmZzZXQiOjF9",
"has_more": true
}
}
Request the next page
Pass the cursor you got back to the same endpoint and keep the same page size. Take the value from the response. Never build or edit cursors.
Stop once has_more is false.
curl "https://app.saga20.com/api/public/v1/characters?limit=1&cursor=eyJvZmZzZXQiOjF9" \
-H "Authorization: Bearer $SAGA20_API_KEY"
{
"data": [
{
"id": "5e2c8a91-7d34-4f0b-a6e1-b03c9d4f1a27",
"name": "Warden Ossric",
"icon": null,
"hidden_from_wiki": false,
"created_at": "2026-09-09T20:15:44+00:00",
"updated_at": "2026-09-18T07:42:30+00:00",
"is_player_character": false
}
],
"pagination": {
"next_cursor": null,
"has_more": false
}
}
Empty pages
When there are no records, or a cursor goes past the end, you get 200 and an empty array.
{
"data": [],
"pagination": {
"next_cursor": null,
"has_more": false
}
}
Ordering and edits
Sessions sort by recording date, newest first, and those without a recording date come last. World entries sort by their manual sort order, then by name. Records that share those values have no guaranteed order.
Pages read the campaign as it stands now. Records may repeat or be missed when entries are added, removed or reordered while you import. Track records by id and begin a new pass if the campaign changes during your read.
Invalid parameters
A limit that fails to parse as a positive integer gives 400 with {"error":"limit must be a positive integer"}. A bad cursor gives 400 with {"error":"Invalid cursor"}. The limit check happens before the cursor check.
Authentication and rate limits run first. These 400 responses use request budget but carry no rate-limit headers.