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, SeatGeek, TickPick, Vivid Seats, Gametime, Viagogo, GoTickets, Paciolan, ATG Tickets, ProVenue, and GoFevo.
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, ticket price API, seat map API, and ticket events 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 request fails the same way it would in production. Responses carry the header Tickets-Sandbox: true. Streaming captures send the same progress events, then the snapshot.
Quickstart
Point the API at any supported event URL. The request runs the capture and returns the whole event: every listing on it, with prices and fees, in one response.
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= | Capture one event: every listing on it, in one response. 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. |
| GET | /v1/events | ?query=&source=&from=&to=&eventId=&page=&pageSize= | Ticket event discovery and matching: search the catalog by name, performer, venue, date, or marketplace URL. Returns one unified event with every matched marketplace ID and URL. Never billed. |
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. Only needed for a host we don't recognize, such as a shortener. See /v1/capture/sources for the values. |
includeVenueMaps | boolean, optional | Render and host the seat maps and fill venueLayout. Off by default, since the maps add time to the capture. Pass true to turn them on. |
Ticket events
/v1/events is the free ticket event discovery and matching API: event name, venue, date, performers, and a sources array of marketplace ids and URLs. Search by name, or pass a marketplace URL to resolve one unified event. It never returns prices, listing counts, or ticket counts. Those stay on capture. See the ticket events API.
One parameter does the searching. ?query= takes a name, a venue or performer, a slug, or any marketplace URL, with or without a scheme, and works out which it is. An event page returns that event; a venue or performer page returns the upcoming dates. Narrow with ?from= / ?to= and ?source=. Native event ids use ?source=ticketmaster&eventId=; ?eventId= also takes the catalog id. To capture listings and prices, take a sources[].url from the response and pass it to /v1/capture?url=.
The catalog is mapped from Ticketmaster, StubHub, SeatGeek, TickPick, Vivid Seats, Gametime, GoTickets, and Paciolan today, with Viagogo, ATG Tickets, ProVenue, and GoFevo rolling out next. Until one lands, /v1/events answers source_not_indexed for it.
| Name | Type | Description |
|---|---|---|
query | string, optional | The one search input. Free text matches event name, venue, city and performers. A hyphenated slug also matches venue and performer url names. A marketplace URL is resolved directly: an event page returns that event, a venue or performer page returns its dates. |
url | string, optional | Alias for query when the value is a URL. Kept so an event link can be passed under an obvious name. |
eventId | string, optional | Native marketplace event id (with source=) or a catalog id. |
source | string, optional | Restrict to events listed on this marketplace, and scope native ids to it. Each event still returns every marketplace carrying it: this filters which events come back, not which links each one shows. |
from | string, optional | Inclusive UTC start bound, ISO-8601. Searches exclude events that have already started unless you pass this; set it to a past date to include them. |
to | string, optional | Exclusive UTC start bound, ISO-8601. |
page | integer, optional | 1-based page number. Defaults to 1, capped at 200. |
pageSize | integer, optional | Events per page. Defaults to 20, capped at 100. |
Responses carry page, pageSize, total, numberOfPages and hasMore above the events array.
Searches return upcoming events. Ones that have already started are left out unless from says otherwise; events with no start time published are always included, since there is nothing to compare. Exact lookups by eventId or by URL are never filtered by date.
Catalog reads require a key, are never billed, and are not rate limited.
curl -G https://api.tickets.dev/v1/events \
--data-urlencode "query=eagles sphere" \
--data-urlencode "from=2026-12-01" \
-H "x-api-key: $TICKETS_DEV_KEY"{
"page": 1,
"pageSize": 20,
"total": 1,
"numberOfPages": 1,
"hasMore": false,
"events": [
{
"id": "01M0FVJHZRFPS6Y3XM5M0BQ1RH",
"name": "The Eagles",
"eventDateLocal": "2026-12-11T20:30:00-08:00",
"eventDateUtc": "2026-12-12T04:30:00.000Z",
"venue": {
"name": "Sphere at The Venetian Resort",
"city": "Las Vegas",
"state": "NV",
"country": "US",
"timezone": "America/Los_Angeles"
},
"performers": [{ "performerId": "44705", "name": "The Eagles", "master": true }],
"sources": [
{
"marketplace": "ticketmaster",
"eventId": "17006483D6FAFC60",
"url": "https://www.ticketmaster.com/eagles-live-at-sphere-las-vegas-nevada-12-11-2026/event/17006483D6FAFC60"
},
{
"marketplace": "vividseats",
"eventId": "7256971",
"url": "https://www.vividseats.com/the-eagles-tickets-las-vegas-sphere-at-the-venetian-resort-12-11-2026--concerts-adult-contemporary/production/7256971"
},
{
"marketplace": "stubhub",
"eventId": "161551904",
"url": "https://www.stubhub.com/the-eagles-las-vegas-tickets-12-11-2026/event/161551904"
},
{
"marketplace": "gotickets",
"eventId": "1794730",
"url": "https://gotickets.com/tickets/1794730/the-eagles-tickets/msg-sphere-las-vegas-nv-12-11-2026"
}
],
"updatedAt": "2026-08-25T09:14:02.418Z"
}
]
}Take any sources[].url from the response and pass it to /v1/capture?url=to get that marketplace's live listings and prices.
The snapshot object
Every capture returns one snapshot: the full listing payload for one event. It carries the event metadata, the currency, the derived stats, the flat list of listings, URLs to the rendered seat maps, and the venue layout.
| 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). |
venueLayout | VenueLayout | The venue map as data: sections, rows and seats. Same includeVenueMaps opt-in as the maps. See The venueLayout object. |
{
"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"
}],
"venueLayout": {
"level": "seat",
"capacity": 16390,
"sections": [{
"section": "101",
"group": "",
"ga": false,
"capacity": 522,
"rows": [{ "row": "34", "capacity": 21, "seats": "1,2,3,…,19,20,21" }]
}]
}
}The stats object
stats is worked out from the listings. Every figure is per ticket and all-in, like totalPrice, and counted per listing: a ten-seat listing counts once. A capture with no listings reports zeroes.
| 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.
| Field | Type | Description |
|---|---|---|
listingId | string | Stable id for the listing. |
inventoryType | string | e.g. primary, resale, verified. |
section | string | Section name or number as the marketplace spells it, with a leading Section word removed, so "Section 101" reads 101 everywhere. The same string as the maps' data-section and venueLayout. Case is the marketplace's own, so casefold when joining across marketplaces. |
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: rows or seats, as fine as the venue's map goes.
The two share a viewBox. The seat file is one group in the section map's coordinates, so append it to the same <svg> when someone zooms in. Sections are tens of kilobytes and seats hundreds, so fetch it only on zoom.
Every region carries data-section, and data-row where the map has rows, spelled the way listings[].section spells it, so a listing binds to its shape by name.
The same parameter fills venueLayout: the sections, rows and seats behind those shapes as data.
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 only a section level map.
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"The venueLayout object
venueLayout is the venue map as data: its sections, the rows in each and the seats in each row. It is read from the same map as the seat maps, for this event's setup of the venue. Pass includeVenueMaps=true to get it; without it the field is { "level": "none", "sections": [] }.
Names match. Sections, rows and seats are spelled the same in venueLayout, in listings and in the maps' data-section, data-row and data-seat.
Seats are in order across the row. A row numbered odd on one side of the aisle and even on the other reads 5,3,1,2,4,6, so neighbours sit next to each other in the string.
level is how deep the map goes. Capacities come at seat level, counted from the seats.
| Field | Type | Description |
|---|---|---|
level | string | How deep the map goes: seat, row, section or none. |
capacity | number, optional | Seats in the venue. Only at seat level. |
sections | Section[] | In the map's order. |
Section
| Field | Type | Description |
|---|---|---|
section | string | Matches listings[].section and data-section. |
group | string | The map's zone for the section, e.g. Lower Bowl. Empty if the map has none. |
ga | boolean | General admission: no rows or seats. |
capacity | number, optional | Seats in the section. Only at seat level. |
rows | Row[] | In the map's order. Empty for GA or when the map stops at sections. |
Row
| Field | Type | Description |
|---|---|---|
row | string | Matches listings[].row and data-row. |
capacity | number, optional | Seats in the row. Only at seat level. |
seats | string | Every seat in the row on the map, comma-joined in order across the row. Matches listings[].seats and data-seat. Empty when the map stops at rows. |
{
"level": "seat",
"capacity": 16390,
"sections": [
{
"section": "101",
"group": "",
"ga": false,
"capacity": 522,
"rows": [
{
"row": "33",
"capacity": 21,
"seats": "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21"
},
{
"row": "36",
"capacity": 15,
"seats": "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"
}
]
}
]
}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/stubhub | stubhub.com | |
/v1/capture/seatgeek | seatgeek.com | |
/v1/capture/tickpick | tickpick.com | |
/v1/capture/vividseats | vividseats.com | |
/v1/capture/gametime | gametime.co | |
/v1/capture/viagogo | viagogo.com | |
/v1/capture/gotickets | gotickets.com | |
/v1/capture/paciolan | evenue.net | |
/v1/capture/atg | atgtickets.com, us.atgtickets.com | |
/v1/capture/provenue | mlb.tickets.com, mpv.tickets.com | |
/v1/capture/gofevo | gofevo.com |
viagogo.de, stubhub.co.uk, ticketmaster.ca and m.ticketmaster.com are all accepted.A URL from a domain we don't recognize works if you name the marketplace with source=; without it the capture returns marketplace_required. A URL from a different marketplace than the one you named returns marketplace_mismatch.
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.",
"outcome": "rejected",
"retryable": false,
"request_id": "6068316c-29d3-447f-b42c-b46f80debd0d"
}
}outcome and retryable say what to do with any error, including codes added after you integrate.
| outcome | What it means | What to do |
|---|---|---|
success | A capture ran and returned an event. There is no error envelope: this is the 200. | Use the snapshot. This is the only outcome that spends a capture. |
not_found | The capture ran, and the marketplace has no event at that URL. | Fix the URL. Retrying returns the same answer more slowly. |
failure | The capture could not complete. Ours, not yours. | Retry. Back off if it persists, then tell us the request_id. |
rejected | The request never became a capture: the key, the URL, the plan, or capacity. | Fix the request or the account. Only rate_limited is worth waiting out. |
/capture/stream the response is committed as 200 before the capture starts, so a failure arrives as an error event carrying these same fields rather than as an HTTP status. Read the event, not the status.| 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. |
success counts against your quota. Failures, rejections and event_not_found are never billed, including the case where a marketplace serves a page for a URL with no event on it, which comes back as 404 event_not_found rather than an empty capture.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. The free ticket events catalog is not rate limited at all.Data use
A capture only reads public data: the listings, prices, fees and seat maps the marketplace shows any visitor on its event page, with no login. Tickets.dev puts no license terms on what a capture returns. Store it, analyze it, build products on it and show it to your own users, with no attribution required.