Skip to main content

Pagination

Research Data API list operations return a pagination object alongside data:

{
"data": [],
"pagination": {
"next_cursor": "eyJvZmZzZXQiOjEwMH0=",
"has_more": true
},
"request_id": "77da54ed-2452-44f7-a857-f9a5ba5e7a51"
}
  • cursor is the optional query parameter used to request the next page. Omit it on the first request.
  • next_cursor is the value to pass as cursor on the next request. It is null after the final page.
  • has_more states whether another page is available.

Cursors are opaque. Do not parse, construct, decode, or modify them. Pass each next_cursor value through exactly as the API returned it.

Retrieve a complete collection​

This Node.js example starts without a cursor and continues until has_more is false:

const accessToken = process.env.OPTIMAL_ACCESS_TOKEN;
const apiBaseUrl = 'https://api.optimalworkshop.com/api/research/v1';

async function fetchAll(path) {
const records = [];
let cursor;

while (true) {
const url = new URL(`${apiBaseUrl}${path}`);

if (cursor) {
url.searchParams.set('cursor', cursor);
}

const response = await fetch(url, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${accessToken}`,
},
});

if (!response.ok) {
throw new Error(`Research Data API request failed with ${response.status}`);
}

const page = await response.json();
records.push(...page.data);

if (!page.pagination.has_more) {
break;
}

cursor = page.pagination.next_cursor;

if (!cursor) {
throw new Error('The API reported another page without returning next_cursor');
}
}

return records;
}

async function main() {
const researchActivities = await fetchAll('/research_activities');
console.log(`Retrieved ${researchActivities.length} research activities`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Checking has_more before reading next_cursor lets the loop end safely when the final next_cursor is absent or null. The additional cursor check prevents an accidental infinite loop if an incomplete pagination envelope is received.

Paginated operations​

Use the same loop for each versioned list operation:

Participant, response, and insight paths also require a research_activity_id path parameter.