Pagination
The Udacity Public API uses cursor-based pagination (Relay-style) for all list queries. This provides stable, efficient pagination even on large datasets.
Cursor-based pagination uses opaque cursor strings instead of page numbers, ensuring stable results even when data changes between requests.
How It Works
Every paginated query returns a connection with three key parts: totalCount, pageInfo, and edges.
Each edge contains a cursor (an opaque string identifying that item’s position) and a node (the actual data).
{
totalCount # Total number of results
pageInfo {
hasNextPage # Are there more results?
endCursor # Cursor to pass as "after" for the next page
}
edges {
cursor # This item's cursor
node {
... # The actual data
}
}
}Basic Pagination
First page
Request the first N items using first. The response includes pageInfo telling you whether more results exist.
query FirstPage($input: ProgramProgressInput!) {
programProgress(input: $input) {
totalCount
pageInfo {
hasNextPage
endCursor
}
edges {
node {
userId
userEmail
}
}
}
}Next page
Use the endCursor from the previous response as the after argument to fetch the next page.
query NextPage($input: ProgramProgressInput!) {
programProgress(input: $input) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
userId
userEmail
}
}
}
}{
"input": {
"first": 50,
"after": "eyJpZCI6IjUwIn0="
}
}Paginating Through All Results
Loop until hasNextPage is false, passing the endCursor from each response as after in the next request.
async function fetchAllProgress(apiKey) {
let allRecords = [];
let cursor = null;
let hasNextPage = true;
while (hasNextPage) {
const response = await fetch('https://api.udacity.com/api/public/api/v1/program-progress/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Token ${apiKey}`,
},
body: JSON.stringify({
query: `
query GetProgramProgress($input: ProgramProgressInput!) {
programProgress(input: $input) {
pageInfo { hasNextPage endCursor }
edges { node { userId userEmail userFirstName userLastName } }
}
}
`,
variables: { input: { first: 100, after: cursor } },
}),
});
const { data } = await response.json();
const { edges, pageInfo } = data.programProgress;
allRecords.push(...edges.map(e => e.node));
hasNextPage = pageInfo.hasNextPage;
cursor = pageInfo.endCursor;
}
return allRecords;
}Page Size Limits
Maximum page size for program progress queries
Maximum page size for assessment progress queries
Maximum page size for learning plan progress queries
Tip: For best performance, use page sizes of 100–500. Larger pages take longer to process and may time out on complex queries.
Tips
- Always check
hasNextPagebefore requesting the next page - Don’t store cursors long-term — they may become invalid if the underlying data changes
- Use
totalCountto show progress indicators or estimate pagination depth - Combine pagination with filters to reduce the total dataset before paginating
Tip: Combine pagination with orderBy and filter to efficiently retrieve exactly the data you need. See Filtering & Sorting for details.