Pagination

Every list endpoint is cursor-paginated. There is no page number and no offset.

The envelope

page object
"page": {
  "next_cursor": "eyJzdGFydEF0IjoiMjAyNi0wOC0xNVQyMDowMDowMFoifQ",
  "has_more": true
}

Pass next_cursor back as ?cursor= to get the following page. On the last page next_cursor is null and has_more is false.

Walking pages
curl -s "https://developer.edmdb.net/api/v1/artists?limit=100" -H "Authorization: Bearer edm_live_..."
# then
curl -s "https://developer.edmdb.net/api/v1/artists?limit=100&cursor=eyJ…" -H "Authorization: Bearer edm_live_..."

Why cursors and not ?page=2

  • Stability. A record inserted while you paginate shifts an offset window, so you silently skip or repeat rows. A cursor is anchored to a position in the sort, not a count.
  • Cost. OFFSET n walks and discards n rows every time, so deep pages get progressively slower. A cursor costs the same on page 500 as on page 1.

Treat cursors as opaque

They are base64url and trivially decodable, but the contents are an implementation detail and will change. Do not construct or parse them — pass back exactly what you were given. A cursor from a different endpoint is rejected with invalid_cursor.

Limits

?limit= defaults to 50 and caps at 100. A value that is absent, non-numeric or non-positive falls back to the default rather than erroring — a rejected request over a cosmetic parameter is worse service than doing the sensible thing.

Filtering and sorting

Filters are explicit per resource rather than a generic query language; the reference lists what each endpoint accepts. Sort order is fixed per endpoint and is part of the pagination contract — events are chronological, most other lists are alphabetical — so there is no ?sort= parameter to conflict with the cursor.

There is also no ?expand=. Related data lives on sub-resources instead — /events/{slug}/lineup, /artists/{slug}/events — which are individually cacheable and keep every response one predictable shape.