Pagination
Three lists are read page by page: leagues, matches and events. The same mechanism serves all three.
Read a page
Two query parameters set the page.
limit: the number of items. It is 25 by default, 100 at most.after: theidof the last item of the previous page. Without it, you read from the start.
A limit above 100 is refused, not trimmed. So is a limit that is not an integer. The API answers INVALID_INPUT.
curl https://gorally.games/v1/matches \
-H "Authorization: Bearer $RALLY_API_KEY"const response = await fetch('https://gorally.games/v1/matches', {
headers: {
Authorization: `Bearer ${process.env.RALLY_API_KEY}`,
},
});
const data = await response.json();import os
import requests
response = requests.get(
"https://gorally.games/v1/matches",
headers={"Authorization": f"Bearer {os.environ['RALLY_API_KEY']}"},
timeout=10,
)
data = response.json()The envelope
Every paginated list returns the same object.
{
"object": "list",
"data": [
{
"id": "f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c",
"categoryId": "e2b4d6f8-1a3c-4e5b-9d7f-0c2e4a6b8d1f",
"status": "SCHEDULED",
"teamAId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"teamBId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"scoreA": null,
"scoreB": null,
"sets": null,
"startsAt": "2027-01-13T00:15:00.000Z",
"revision": 3
}
],
"has_more": true
}objectis alwayslist.dataholds the page’s items.has_moreistruewhen items remain after this page.
Walk the whole list
- Read the first page, without
after. - Process
data. - If
has_moreistrue, read again withafterset to theidof the last item indata. Ifdatais empty, keep the sameafter, wait a moment and read again. - Stop when
has_moreisfalse.
let after;
let hasMore = true;
while (hasMore) {
const url = new URL('https://gorally.games/v1/leagues');
url.searchParams.set('limit', '100');
if (after) url.searchParams.set('after', after);
const page = await (await fetch(url, {
headers: { Authorization: `Bearer ${process.env.RALLY_API_KEY}` },
})).json();
for (const league of page.data) console.log(league.name);
hasMore = page.has_more;
if (page.data.length > 0) after = page.data.at(-1).id;
else if (hasMore) await new Promise((resolve) => setTimeout(resolve, 1000));
}
An unknown cursor is refused with INVALID_INPUT. Rally does not restart from the beginning: you would receive a page twice. Do not build a cursor, copy an id you received.
What stays stable
The walk follows an internal insertion number that nothing moves. Renaming a league or correcting a match between two pages neither hides it nor brings it back.
A write whose transaction is still in flight is delayed, never skipped. It appears in its place once committed, exactly once. A page can therefore be shorter than limit while has_more is true: keep going. When that write sits right after your cursor, data is even empty with has_more at true. Nothing is lost: read again a little later with the same after.
Complete lists
A season’s categories and a category’s standings return all their items at once. They take neither limit nor after, and their envelope has no has_more.
What next
- Read the “Limits” guide: a loop of pages counts against your quota.