API Changelog
Dated record of additive changes to the SeatData REST API
Changes to the SeatData REST API, newest first. Entries here are additive unless stated otherwise.
2026-09-30 — Listings: GET /api/v1/events/{event_id}/listings
The listings endpoint now has a v1 equivalent. GET /api/v0.1.1/listings/get (and v0.1) stay
live and fully supported; nothing changes for existing callers. Billing is unchanged: a first page
is charged only when the event's listings refreshed since your last charged pull, continuation
pages are free, and refresh state is shared between v0.1.1 and v1.
What v1 adds over v0.1.1:
| v0.1.1 | v1 | |
|---|---|---|
| Event selector | ?event_id= / ?event_id_sh= | path id, ?id_type=marketplace for a Marketplace Event ID |
| Envelope | {has_refreshed, last_refresh_timestamp, listings} | {event_id, seatdata_event_id, sh_event_id, vs_event_id, data, has_more, next_cursor, total_count, has_refreshed, last_refreshed_at} |
| Pagination | none | cursor pages, limit default 500, max 1000 |
| Filter | none | ?active=true / ?active=false |
| Rows | active as 0/1 | active as a boolean, plus created_at and updated_at |
| Errors | plain text | JSON error envelope |
See the migration guide.
2026-09-24 — Sales endpoints: SeatData and Marketplace Event IDs on every response
Both v1 sales endpoints now identify each served event by its SeatData Event ID and by its
Marketplace Event ID on each marketplace SeatData observes. This update is additive: no request
parameter, pagination, source, id_type, rate limit, or billing change, and no new URL version.
Row shape is unchanged. No existing integration needs to change.
Affected endpoints:
GET /api/v1/events/{event_id}/salesPOST /api/v1/events/sales/batch
On the single-event endpoint the three keys sit directly after event_id on every page, unlike
total_count and sources, which stay first-page only:
| Field | Type | Meaning |
|---|---|---|
seatdata_event_id | integer | Always the SeatData Event ID, whatever id style the caller used. |
sh_event_id | integer | null | The event's Marketplace Event ID on the sh marketplace. null when SeatData has not matched the event there. |
vs_event_id | integer | null | The event's Marketplace Event ID on the vs marketplace. null when unmatched. Informational only; no endpoint accepts it as input. |
The batch endpoint adds one top-level events object, keyed exactly like results and sources
by the identifier sent in the request. Each value carries the same three fields. Only served
events get an entry: an id that lands in errors has none. A served event_ids_sh value with no
matching SeatData event gets seatdata_event_id: null with sh_event_id set to the sent id.
event_id still echoes the id from the request path; with ?id_type=marketplace it equals
sh_event_id, and seatdata_event_id gives the internal id.
2026-09-24 — Event search: sh_event_id and vs_event_id on every result
Every item returned by event search now carries the event's Marketplace Event ID on each marketplace SeatData observes. This update is additive: no request parameter, pagination, rate limit, or billing change, and no new URL version. Search remains free. No existing integration needs to change.
Affected endpoint:
GET /api/v1/events/search
The two fields appear directly after tm_event_id in every data item:
| Field | Type | Meaning |
|---|---|---|
sh_event_id | integer | null | The event's Marketplace Event ID on the sh marketplace. null when SeatData has not matched the event there. |
vs_event_id | integer | null | The event's Marketplace Event ID on the vs marketplace. null when SeatData has not matched the event there. Informational only; no endpoint accepts this id as input. |
Both values are JSON integers, unlike tm_event_id, which is a string.
sh_event_id can be passed straight to the sales endpoints as a Marketplace Event ID: as the
path id on GET /api/v1/events/{event_id}/sales with ?id_type=marketplace, and in
event_ids_sh on POST /api/v1/events/sales/batch and POST /api/v0.3/salesdata/batch.
The legacy v0.3 and v0.3.1 search endpoints do not gain the fields.
2026-09-17 — Sales endpoints: optional source filter, new row fields, per-event sources metadata
The v1 sales endpoints now return sales observed on more than one marketplace. The URL version does not change, and no existing request changes: this update adds optional inputs and new output fields.
Affected endpoints:
GET /api/v1/events/{event_id}/salesPOST /api/v1/events/sales/batch
The default is unchanged
Omitting source, or sending source=sh, returns exactly the rows and charges you received
before this update. No existing integration needs to change.
Optional source query parameter
Accepted on both endpoints as a query-string parameter. On the batch endpoint it goes on the URL, not in the JSON body.
source | Returns |
|---|---|
omitted, or sh | The default. Unchanged rows, unchanged charges. |
vs | Only sales observed on the second marketplace. |
all | Both, merged newest-first. |
Any other value returns 400 with error.code set to invalid_param and error.param set to
source. That response is free — it is rejected before any billing or data read.
Pagination cursors are bound to the source they were issued under. Replaying a cursor under a
different source returns 400 with error.code set to invalid_cursor. Use one source value
for every page of a walk.
New fields on every sale row
Two fields are added to every row, on both endpoints and under every source value:
| Field | Type | Meaning |
|---|---|---|
source | string | "sh" or "vs" — the marketplace the sale was observed on. |
all_in_price | number | null | Fee-inclusive price the marketplace showed. Always null on sh rows. |
A vs row carries two more. sh rows carry neither, because their names are the naming space
vs rows are translated into and have nothing to translate.
| Field | Type | Meaning |
|---|---|---|
norm_zone | string | The zone name used for the same seats in sh sales for that event or venue. Derived by matching, not reported by the marketplace. "" when no match is found. |
norm_section | string | The same, for the section. |
Row key sets differ by source
Read source to tell which shape a row has. On a sh row, listing_id is an integer,
all_in_price is always null, and the normalized fields are absent. On a vs row,
listing_id is a string, zone is always "", section may combine zone and section into one
label, and both normalized fields are present.
To join the two sets, compare a vs row's norm_section against a sh row's
section, and its norm_zone against zone. That is what the fields are for.
timestamp, quantity, price, zone, section, row, and listing_id are otherwise
unchanged on sh rows. vs sales come from listing changes
between pulls, so an event's vs count is a lower bound. Sales whose seat details could not be
determined are not returned, and are not counted in total_count.
Quantity and price
sh sales often reach us unenriched, without a quantity or price. For those we derive both from
the listing data we hold for that listing; a quantity of 0 means it could not be reliably
determined. vs sales carry both as reported.
New sources metadata
The single-event endpoint returns a top-level sources array on the first page. Continuation pages
omit it. The batch endpoint returns a top-level sources object keyed by the same client-sent event
ids as results; results and errors are unchanged in shape.
sources always carries two entries, sh then vs, whatever source you sent. Each
entry has:
source—"sh"or"vs"collecting_since— ISO date of the earliest sale observed for this event on that marketplace, ornull. It counts sales these endpoints do not return, so it can be older than your oldest returned row.tracked_for_event— whether the event is tracked on that marketplacestatus—"ok", or"unavailable"when that marketplace could not be read for this request. The response still succeeds, carrying the rows that were available.
total_count on the first page counts rows under the active source filter.
Billing
Unchanged. A first page that returns at least one row is one pull, whichever source produced it.
Continuation pages are free. A page with no rows is free. Because the default is sh, a caller
who sends no source pays exactly what they paid before.
Not changed
No new URL version. The v0.x endpoints are untouched and remain single-marketplace. The error
envelope, cursor format, limit bounds, rate limits, and authentication are all unchanged.