ViewRoyal.ai API Docs

Pagination

Navigate through large result sets with cursor-based and page-based pagination

The ViewRoyal.ai API uses two pagination systems depending on the endpoint family:

  • v1 API endpoints (meetings, people, matters, motions, bylaws) use cursor-based pagination
  • OCD API endpoints (people, organizations, events, bills, votes, jurisdictions) use page-based pagination
  • Search endpoint is a hybrid -- it accepts page-based parameters but returns the v1 response envelope

Cursor-Based Pagination (v1 API)

All v1 list endpoints use cursor-based pagination with two query parameters:

ParameterDefaultMaxDescription
per_page20100Number of items per page
cursor(omit for first page)--Opaque cursor from previous response

Response Shape

{
  "data": [
    { "id": 42, "slug": "2024-01-15-regular-council", "title": "Regular Council Meeting", "date": "2024-01-15" },
    { "id": 41, "slug": "2024-01-08-committee-of-the-whole", "title": "Committee of the Whole Meeting", "date": "2024-01-08" }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJ2IjoiMjAyNC0wMS0wOCIsImlkIjo0MX0=",
    "per_page": 2
  },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

How to Paginate

  1. Make your first request without a cursor parameter
  2. Check pagination.has_more in the response
  3. If true, pass pagination.next_cursor as the cursor query parameter in your next request
  4. If false, you have reached the last page

Example: Fetching Two Pages

# Page 1: no cursor
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://viewroyal.ai/api/v1/view-royal/meetings?per_page=5"

# Page 2: use next_cursor from page 1 response
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://viewroyal.ai/api/v1/view-royal/meetings?per_page=5&cursor=eyJ2IjoiMjAyNC0wMS0wOCIsImlkIjo0MX0="
const API_KEY = 'YOUR_API_KEY';
const headers = { 'X-API-Key': API_KEY };

// Page 1
const page1 = await fetch(
  'https://viewroyal.ai/api/v1/view-royal/meetings?per_page=5',
  { headers }
);
const json1 = await page1.json();
console.log(`Page 1: ${json1.data.length} meetings`);

// Page 2 (if more results exist)
if (json1.pagination.has_more) {
  const page2 = await fetch(
    `https://viewroyal.ai/api/v1/view-royal/meetings?per_page=5&cursor=${json1.pagination.next_cursor}`,
    { headers }
  );
  const json2 = await page2.json();
  console.log(`Page 2: ${json2.data.length} meetings`);
}
import requests

API_KEY = 'YOUR_API_KEY'
headers = {'X-API-Key': API_KEY}

# Page 1
resp1 = requests.get(
    'https://viewroyal.ai/api/v1/view-royal/meetings',
    headers=headers,
    params={'per_page': 5}
)
json1 = resp1.json()
print(f"Page 1: {len(json1['data'])} meetings")

# Page 2 (if more results exist)
if json1['pagination']['has_more']:
    resp2 = requests.get(
        'https://viewroyal.ai/api/v1/view-royal/meetings',
        headers=headers,
        params={'per_page': 5, 'cursor': json1['pagination']['next_cursor']}
    )
    json2 = resp2.json()
    print(f"Page 2: {len(json2['data'])} meetings")

Cursors are opaque tokens. Never attempt to decode, construct, or modify them. Always use the exact next_cursor value from the previous response.

Full Iteration: Fetching All Pages

To retrieve every item from a list endpoint, loop until has_more is false:

async function fetchAll(endpoint, apiKey) {
  const headers = { 'X-API-Key': apiKey };
  let cursor = null;
  let allItems = [];

  do {
    const url = new URL(`https://viewroyal.ai${endpoint}`);
    url.searchParams.set('per_page', '20');
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, { headers });
    const json = await res.json();

    allItems.push(...json.data);
    cursor = json.pagination.next_cursor;
  } while (cursor);

  return allItems;
}

// Fetch all meetings
const meetings = await fetchAll('/api/v1/view-royal/meetings', 'YOUR_API_KEY');
console.log(`Total meetings: ${meetings.length}`);
import requests

def fetch_all(endpoint, api_key):
    headers = {'X-API-Key': api_key}
    cursor = None
    all_items = []

    while True:
        params = {'per_page': 20}
        if cursor:
            params['cursor'] = cursor

        resp = requests.get(
            f'https://viewroyal.ai{endpoint}',
            headers=headers,
            params=params
        )
        json_data = resp.json()

        all_items.extend(json_data['data'])
        cursor = json_data['pagination']['next_cursor']

        if not cursor:
            break

    return all_items

# Fetch all meetings
meetings = fetch_all('/api/v1/view-royal/meetings', 'YOUR_API_KEY')
print(f"Total meetings: {len(meetings)}")

Page-Based Pagination (OCD API)

All OCD list endpoints use traditional page-based pagination with two query parameters:

ParameterDefaultMaxDescription
page1--Page number (1-based)
per_page20100Number of items per page

Response Shape

{
  "results": [
    { "id": "ocd-person/abc123", "name": "Jane Councillor", "current_role": "Councillor" },
    { "id": "ocd-person/def456", "name": "John Mayor", "current_role": "Mayor" }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "max_page": 3,
    "total_items": 47
  }
}

How to Paginate

  1. Start with page=1 (or omit it -- defaults to 1)
  2. Check pagination.max_page to know the total number of pages
  3. Increment page until page >= max_page
  4. Use total_items to show progress to users (e.g., "Showing 1-20 of 47")

Example

# Page 1
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://viewroyal.ai/api/ocd/view-royal/people?page=1&per_page=10"

# Page 2
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://viewroyal.ai/api/ocd/view-royal/people?page=2&per_page=10"
const API_KEY = 'YOUR_API_KEY';
const headers = { 'X-API-Key': API_KEY };

const res = await fetch(
  'https://viewroyal.ai/api/ocd/view-royal/people?page=1&per_page=10',
  { headers }
);
const json = await res.json();

console.log(`Page ${json.pagination.page} of ${json.pagination.max_page}`);
console.log(`Showing ${json.results.length} of ${json.pagination.total_items} total`);
import requests

headers = {'X-API-Key': 'YOUR_API_KEY'}

resp = requests.get(
    'https://viewroyal.ai/api/ocd/view-royal/people',
    headers=headers,
    params={'page': 1, 'per_page': 10}
)
json_data = resp.json()

page = json_data['pagination']['page']
max_page = json_data['pagination']['max_page']
total = json_data['pagination']['total_items']
print(f"Page {page} of {max_page} ({total} total items)")

OCD detail endpoints (e.g., GET /api/ocd/view-royal/people/{id}) are not paginated. They return the full entity directly at the top level with no wrapper object.

Search Endpoint (Hybrid)

The search endpoint (GET /api/v1/view-royal/search) uses a hybrid approach:

  • Parameters: Page-based (page and per_page), same as OCD endpoints
  • Response envelope: v1-style (data, pagination, meta), same as v1 endpoints
  • Cursor: The next_cursor field is always null -- use the page parameter to navigate
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://viewroyal.ai/api/v1/view-royal/search?q=budget&page=1&per_page=10"
{
  "data": [
    { "type": "motion", "score": 0.1, "title": "Budget Approval Motion", "snippet": "..." }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": null,
    "per_page": 10,
    "page": 1
  },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

To paginate search results, increment the page parameter and check has_more. See the Search endpoint reference for full query parameters and response fields.

Quick Reference

Featurev1 EndpointsOCD EndpointsSearch
StyleCursor-basedPage-basedPage-based
Parameterscursor, per_pagepage, per_pagepage, per_page
Default per_page202020
Max per_page100100100
Envelopedata + pagination + metaresults + paginationdata + pagination + meta
Next page signalhas_more + next_cursorpage < max_pagehas_more

On this page