Concepts

Pagination

How lists are paged with a cursor, how to walk them, and how to keep a copy in sync.

Every list operation returns one page of objects at a time. You move from page to page with a cursor: the id of an object you already have.

The list object

A list answers a list object:

Response
{
  "object": "list",
  "url": "/v1/registrations",
  "has_more": true,
  "data": [
    {
      "id": "SUB-8c3e2a71-0f4b-4d6a-b5e9-27c1d9a4e612",
      "object": "registration",
      "event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30",
      "status": "validated",
      "role": "attendee",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada.lovelace@example.com",
      "ticket": {
        "name": "Full pass"
      },
      "checkin": {
        "checked_in": true,
        "last_checked_in_at": 1810119600
      },
      "created": 1795521600,
      "updated": 1810119600
    }
  ]
}
  • data: the objects of the page, [] when there are none.
  • has_more: true when more objects follow this page.
  • url: the path of the list.

Objects always come newest first: by created descending, then by id descending.

Page size

limit sets how many objects a page holds: from 1 to 100, 10 by default. With any expand[] in the request, limit is at most 20; above that, the request answers 400 parameter_invalid on limit.

Request

curl

curl -G https://api.masterconf.com/v1/registrations \
  -H "Authorization: Bearer mcf_your_key" \
  --data-urlencode "event=EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30" \
  --data-urlencode "limit=100"

JavaScript

const params = new URLSearchParams({
  "event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30",
  "limit": "100",
});

const res = await fetch(`https://api.masterconf.com/v1/registrations?${params}`, {
  headers: { Authorization: "Bearer mcf_your_key" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.masterconf.com/v1/registrations",
    params={"event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30", "limit": "100"},
    headers={"Authorization": "Bearer mcf_your_key"},
)
data = res.json()

The next page

Send starting_after with the id of the last object of the page you have. The answer holds the objects that follow it.

Request

curl

curl -G https://api.masterconf.com/v1/registrations \
  -H "Authorization: Bearer mcf_your_key" \
  --data-urlencode "event=EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30" \
  --data-urlencode "starting_after=SUB-8c3e2a71-0f4b-4d6a-b5e9-27c1d9a4e612"

JavaScript

const params = new URLSearchParams({
  "event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30",
  "starting_after": "SUB-8c3e2a71-0f4b-4d6a-b5e9-27c1d9a4e612",
});

const res = await fetch(`https://api.masterconf.com/v1/registrations?${params}`, {
  headers: { Authorization: "Bearer mcf_your_key" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.masterconf.com/v1/registrations",
    params={"event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30", "starting_after": "SUB-8c3e2a71-0f4b-4d6a-b5e9-27c1d9a4e612"},
    headers={"Authorization": "Bearer mcf_your_key"},
)
data = res.json()

To walk a whole list:

  1. Ask for the first page.
  2. Process its data.
  3. While has_more is true, ask again with starting_after set to the id of the last object of data.

The previous page

Send ending_before with the id of the first object of the page you have. The page holds the objects that precede it, in the same newest-first order. starting_after and ending_before never go together: sending both answers parameter_invalid.

The total count

include[]=total_count adds total_count, the number of objects of the whole list. It is absent unless you ask for it.

Request

curl

curl -G https://api.masterconf.com/v1/registrations \
  -H "Authorization: Bearer mcf_your_key" \
  --data-urlencode "event=EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30" \
  --data-urlencode "include[]=total_count"

JavaScript

const params = new URLSearchParams({
  "event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30",
  "include[]": "total_count",
});

const res = await fetch(`https://api.masterconf.com/v1/registrations?${params}`, {
  headers: { Authorization: "Bearer mcf_your_key" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.masterconf.com/v1/registrations",
    params={"event": "EVT-5a1f0c2e-3b7d-4e91-9c2a-1d6e8f4b7a30", "include[]": "total_count"},
    headers={"Authorization": "Bearer mcf_your_key"},
)
data = res.json()

Valid cursors

A cursor must be an object of the same list: of your organization and of the list’s parent. That is the event of a list of registrations, the call_for_papers of a list of papers, and the event filter of a list of calls for papers, when you give one. Any other id, unknown or removed, answers parameter_invalid on its parameter. The API never silently starts again from the top.

A cursor object that no longer matches the list’s filters still works. If a registration you used as a cursor changes status while you walk status=require_admin_validation, the next page starts from where it was.

Keeping a copy in sync

To pick up the objects created since your last run, walk the list from the newest and stop at the first id you already have.

Objects created while you walk sit before your cursor, so they never shift the pages that follow. They appear at the top on your next run.

This finds new objects only. A change to an object you already have, such as a new status, does not move it in the list: read it again with its retrieve operation, or walk the whole list. v1 has no filter on updated.

What costs more

The cheapest call is the page that follows the one you just read. Keep walking forward with starting_after.

  • A cursor far down a long list, that you did not get from a recent page, costs the API more reads to place. The call is slower.
  • Many objects created in the same second, as an import does, are slower to page through.

A known limit

An object can enter a filtered list with an old created date, for example an older registration whose status changes to the one you filter on. If that happens at the very moment of one of your calls, that call can miss it. Your next full walk will see it.