API reference
API reference
tickets.dev is a real-time ticket scraping API for retrieving live ticket inventory, pricing, fees, sections, rows, quantities, and seat maps from Ticketmaster, StubHub, Vivid Seats, SeatGeek, TickPick, Gametime, Viagogo, and GoTickets.
Introduction
The tickets.dev API scrapes live marketplace inventory on demand. There is no crawl to schedule and no cache to go stale: each call opens the event, reads its current listings and prices, and returns the result to you. Ask for the seat maps and it renders those too.
Learn the product concepts in the ticket scraping API, ticket inventory API, and ticket price API guides.
All endpoints live under a single base URL and speak JSON.
https://api.tickets.dev/v1Authentication
Every request must carry your API key. Send it in the x-api-key header. Create a key from the account page.
curl -G https://api.tickets.dev/v1/capture/ticketmaster \
--data-urlencode "url=$EVENT_URL" \
-H "x-api-key: $TICKETS_DEV_KEY"An apiKeyquery parameter is accepted as well, for callers that can't set a header.
Sandbox
There is no separate sandbox host. Same base URL, same paths, same query parameters, same snapshot shape. One public key returns fixtures instead of a live capture.
tk_test_sandboxBuild against that key. Cut over by swapping it for your org's tk_live_… key. Nothing else in the request should have to change: not the URL, not the query, not the parser.
curl -G https://api.tickets.dev/v1/capture/ticketmaster \
--data-urlencode "url=https://www.ticketmaster.com/event/example" \
-H "x-api-key: tk_test_sandbox"Validation still runs, so a bad URL, a marketplace mismatch, or a restricted source fails the same way it would in production. The JSON body does not advertise sandbox, because that would leak into client parsers. Responses include the header Tickets-Sandbox: true. Streaming captures send the same progress events, then a snapshot event with that same body.
Quickstart
Point the API at any supported event URL. The request runs the capture and returns the finished result.
curl -G https://api.tickets.dev/v1/capture/ticketmaster \
--data-urlencode "url=https://www.ticketmaster.com/event/XXXXXXXX" \
-H "x-api-key: $TICKETS_DEV_KEY"Endpoints
Pass an event URL and every marketplace returns the identical snapshotobject. The marketplace is optional: it comes from the URL's host unless you name it with source= or in the path.
| Method | Path | Query | Returns |
|---|---|---|---|
| GET | /v1/capture | ?url=&source=&includeVenueMaps= | Run a capture. A live key spends one credit; tk_test_sandbox returns a fixture and is never billed. Costs one capture with a live key. |
| GET | /v1/capture/{source} | ?url=&includeVenueMaps= | The same capture with the marketplace in the path, e.g. /v1/capture/ticketmaster. Costs one capture with a live key. |
| GET | /v1/capture/sources | - | The marketplaces you can capture, and the hosts that route to each. Never billable. |
| GET | /v1/health | - | Service status. No key required. |
Parameters
| Name | Type | Description |
|---|---|---|
url | string, required | The event page URL from a supported marketplace. |
source | string, optional | Which marketplace to capture from, e.g. ticketmaster. Not needed when the URL's host already identifies one, which is the usual case. Pass it for a host we don't recognize, such as a shortener or a regional domain. all means the same as leaving it off. See /v1/capture/sources for the values. |
includeVenueMaps | boolean, optional | Render and host the seat maps for this capture. Off by default: the maps add render and upload time to a request that most callers make for pricing alone, so sectionLevelUrl and seatLevelUrl come back empty unless you ask. Pass true (or 1/yes) to turn them on. |
The snapshot object
Every capture returns one snapshot. It carries the event metadata, the currency, the derived stats, the flat list of listings, and URLs to the rendered seat maps.
| Field | Type | Description |
|---|---|---|
source | string | Marketplace the capture came from, e.g. ticketmaster. |
eventId | string | The marketplace's own id for the event. |
eventName | string | Event title. |
performers | Performer[] | Acts or teams on the bill, each with performerId, name, and master. |
venueId | string | The marketplace's own venue id. |
venueName | string | Venue name. |
venueAddress | string | Street address of the venue. |
venueCity | string | Venue city. |
venueState | string | Venue state or province. |
venueTimezone | string | IANA zone of the venue, e.g. America/Chicago. Empty when the marketplace doesn't publish one. |
eventDateLocal | string | Event date/time in venue-local ISO 8601. |
eventDateUtc | string | The same moment in ISO 8601 UTC. |
sourceUrl | string | The event page the capture read. |
capturedAt | string | When the capture ran, ISO 8601 UTC. |
currency | string | ISO currency code for every price, e.g. USD. |
note | string | The marketplace's own buyer notice for this event. An event with nothing to sell (sold out, canceled, or not yet on sale) is a normal 200 with an empty listings array and the reason here. |
sectionLevelUrl | string | Section-level overview SVG. Empty unless the request passed includeVenueMaps=true. See Seat maps. |
seatLevelUrl | string | Zoomed seat-level SVG. Same includeVenueMaps opt-in as above. |
stats | Stats | Whole-event pricing we derive from listings (see below). |
listings | Listing[] | Every available listing (see below). |
{
"source": "ticketmaster",
"eventId": "17006483D6FAFC60",
"eventName": "Eagles Live at Sphere",
"venueId": "189524",
"venueName": "Sphere",
"venueCity": "Las Vegas",
"venueState": "NV",
"venueTimezone": "America/Los_Angeles",
"eventDateLocal": "2026-12-11T20:30:00-08:00",
"eventDateUtc": "2026-12-12T04:30:00+00:00",
"currency": "USD",
"sectionLevelUrl": "https://storage.tickets.dev/…/section.svg",
"seatLevelUrl": "https://storage.tickets.dev/…/seats.svg",
"stats": {
"listingCount": 136,
"ticketCount": 1037,
"getInPrice": 303.08,
"medianPrice": 710.95,
"avgPrice": 746.14,
"maxPrice": 2309.85
},
"listings": [{
"listingId": "",
"inventoryType": "primary",
"section": "208",
"row": "9",
"quantity": 3,
"ticketPrice": 1157.5,
"fee": 208.35,
"totalPrice": 1365.85,
"sellableQuantities": "1,2,3"
}]
}The stats object
Everything else in a snapshot is what the marketplace said; stats is what we worked out from it. Every figure is per-ticket and all-in, the same basis as totalPrice, so they compare cleanly across marketplaces, and every statistic is per listing, not per seat: a ten-seat listing counts once, exactly as it appears once on the marketplace. A capture with no listings reports zeroes, never null.
| Field | Type | Description |
|---|---|---|
listingCount | number | How many listings the capture returned. |
ticketCount | number | Total seats across every listing: how deep the inventory is. |
getInPrice | number | Cheapest all-in price per ticket: the get-in price. |
medianPrice | number | Median all-in price per ticket, across listings. |
avgPrice | number | Average all-in price per ticket, across listings. |
maxPrice | number | Highest all-in price per ticket. |
The listing object
Each entry in listings is one group of same-price seats. Every field below comes back from every marketplace; a source that also exposes something of its own (a deal score, a seat-view photo, a map pin) adds it alongside them.
| Field | Type | Description |
|---|---|---|
listingId | string | Stable id for the listing. |
inventoryType | string | e.g. primary, resale, verified. |
section | string | Section name or number. |
row | string | Row label. |
quantity | number | Seats available in this listing. |
ticketPrice | number | Face price per ticket, before fees. |
fee | number | Per-ticket fees. |
totalPrice | number | All-in price per ticket, excluding sales tax. Normalized to the same meaning on every marketplace. |
ticketType | string | How the ticket is delivered, e.g. Mobile Transfer. |
sellableQuantities | string | Comma-separated quantities you can actually buy, e.g. 2,4. |
group | string | The marketplace's own seating tier, e.g. Upper. |
seats | string | Comma-separated seat numbers, when exposed by the source. |
listingNotes | string | Seller notes, e.g. Aisle seat. |
dealScore | string | The marketplace's own value-for-money score, as it shows it. Empty where the source doesn't publish one, and not comparable between marketplaces. |
Seat maps
Pass includeVenueMaps=true and the capture renders two SVGs and hosts them for you:
sectionLevelUrl, the overview: the venue outline, the stage, and one shape per section.seatLevelUrl, the zoomed detail, as fine as that marketplace draws it: one mark per seat on Ticketmaster and SeatGeek, one shape per row on StubHub, viagogo and TickPick.
The two share a viewBox. The seat file is a single group in the section map's own coordinates, so you render the overview and append the detail to the same <svg> when someone zooms in: no transform, and a viewer that never zooms never fetches it. That split matters: a venue's sections are tens of kilobytes, its seats hundreds.
Every region on both carries data-section, and data-row where the source has rows, spelled exactly the way listings[].section spells it in the same response, so you can bind a listing to its shape by name and shade it by price or availability without a lookup table.
Without the parameter both fields come back as empty strings. The fields are always present, so a client never has to branch on their existence. seatLevelUrl is also empty where the marketplace publishes nothing below the section, which is the case on Vivid Seats and Gametime. Either way the rest of the snapshot comes back normally: a map that can't be rendered never fails a capture.
curl -G https://api.tickets.dev/v1/capture/ticketmaster \
--data-urlencode "url=$EVENT_URL" \
--data-urlencode "includeVenueMaps=true" \
-H "x-api-key: $TICKETS_DEV_KEY"Marketplaces
Every marketplace returns the same snapshot. Pass an event URL from one of its domains and the marketplace is worked out for you. The paths below are the explicit form, and the last segment of each is also the value for source=:
| Marketplace | Path | Event URL domains |
|---|---|---|
/v1/capture/ticketmaster | ticketmaster.com, livenation.com | |
/v1/capture/vividseats | vividseats.com | |
/v1/capture/stubhub | stubhub.com | |
/v1/capture/seatgeek | seatgeek.com | |
/v1/capture/tickpick | tickpick.com | |
/v1/capture/gametime | gametime.co | |
/v1/capture/viagogo | viagogo.com | |
/v1/capture/gotickets | gotickets.com | |
/v1/capture/paciolan | evenue.net |
viagogo.de, stubhub.co.uk, ticketmaster.ca and m.ticketmaster.com are all accepted. The table lists the canonical domain, not an exhaustive list.An event URL from a domain we don't recognize is still accepted, so regional partners and link shorteners work, as long as you name the marketplace, since there is nothing to infer from otherwise. Without a name such a URL returns marketplace_required. And a URL that clearly belongs to a different marketplace than the one you named is rejected with marketplace_mismatch rather than captured against the wrong source.
Errors
Every error carries a stable code and the request_id that also comes back in the x-request-id header. Branch on the code, not the status: several distinct failures share a status, and the code is what tells you whether retrying is worth it.
{
"error": {
"code": "marketplace_mismatch",
"message": "Requested source \"stubhub\" but the URL is a ticketmaster event.",
"request_id": "6068316c-29d3-447f-b42c-b46f80debd0d"
}
}| Status | Code | Meaning |
|---|---|---|
400 | missing_url | No url was given. |
400 | invalid_url | url is not a valid http(s) URL. |
400 | marketplace_required | The URL's host is not one we recognize, so name the marketplace with source=. |
400 | marketplace_mismatch | The source you named and the URL disagree. |
401 | missing_key | No API key was sent. |
401 | invalid_key | The key is unknown or revoked. |
402 | no_plan | No captures remaining. Choose a plan to continue. |
402 | subscription_inactive | The subscription lapsed. Update the payment method. |
404 | unknown_marketplace | No such marketplace. See /v1/capture/sources. |
404 | event_not_found | The marketplace has no event at that URL. Check the URL: retrying returns the same answer. |
429 | quota_exceeded | No captures left on the balance. |
429 | rate_limited | Too many captures in flight, or capacity is saturated. Honour Retry-After. |
502 | capture_failed | The capture could not complete. |
503 | capture_unavailable | Our side is down, not your rate. Safe to retry. |
504 | capture_timeout | The capture did not finish in time. Safe to retry. |
200 counts against your quota.Rate limits
Each plan adds captures each invoice and sets a ceiling on how many captures can run at once. Spending them returns 429 quota_exceeded; whatever is left stays on your balance. See the pricing table for the limits on every tier.
429 rate_limited means slow down for a moment, not that you are out of quota: either too many of your captures are already running, or capture capacity is momentarily full. It carries a Retry-After header and is never billed. Honour the header and retry.