tickets.dev

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.

Base URL
https://api.tickets.dev/v1
Same schema, every source. A Ticketmaster capture and a Vivid Seats capture return the identical snapshot object, so you never branch on the marketplace.

Authentication

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.

Keep your key server-side. It carries your full quota, so never embed it in client-side code or commit it to a repo. Rotate a key immediately if it leaks. The public sandbox key below cannot spend credits.

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.

Sandbox API key
tk_test_sandbox

Build 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.

Sandbox request
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.

Start in the playground. Sandbox mode uses this key against the real API. Copy the snippet, then replace the key with yours when you go live.

Quickstart

Point the API at any supported event URL. The request runs the capture and returns the finished result.

cURL
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.

MethodPathQueryReturns
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

NameTypeDescription
urlstring, requiredThe event page URL from a supported marketplace.
sourcestring, optionalWhich 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.
includeVenueMapsboolean, optionalRender 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.

FieldTypeDescription
sourcestringMarketplace the capture came from, e.g. ticketmaster.
eventIdstringThe marketplace's own id for the event.
eventNamestringEvent title.
performersPerformer[]Acts or teams on the bill, each with performerId, name, and master.
venueIdstringThe marketplace's own venue id.
venueNamestringVenue name.
venueAddressstringStreet address of the venue.
venueCitystringVenue city.
venueStatestringVenue state or province.
venueTimezonestringIANA zone of the venue, e.g. America/Chicago. Empty when the marketplace doesn't publish one.
eventDateLocalstringEvent date/time in venue-local ISO 8601.
eventDateUtcstringThe same moment in ISO 8601 UTC.
sourceUrlstringThe event page the capture read.
capturedAtstringWhen the capture ran, ISO 8601 UTC.
currencystringISO currency code for every price, e.g. USD.
notestringThe 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.
sectionLevelUrlstringSection-level overview SVG. Empty unless the request passed includeVenueMaps=true. See Seat maps.
seatLevelUrlstringZoomed seat-level SVG. Same includeVenueMaps opt-in as above.
statsStatsWhole-event pricing we derive from listings (see below).
listingsListing[]Every available listing (see below).
Response
{
  "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.

FieldTypeDescription
listingCountnumberHow many listings the capture returned.
ticketCountnumberTotal seats across every listing: how deep the inventory is.
getInPricenumberCheapest all-in price per ticket: the get-in price.
medianPricenumberMedian all-in price per ticket, across listings.
avgPricenumberAverage all-in price per ticket, across listings.
maxPricenumberHighest 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.

FieldTypeDescription
listingIdstringStable id for the listing.
inventoryTypestringe.g. primary, resale, verified.
sectionstringSection name or number.
rowstringRow label.
quantitynumberSeats available in this listing.
ticketPricenumberFace price per ticket, before fees.
feenumberPer-ticket fees.
totalPricenumberAll-in price per ticket, excluding sales tax. Normalized to the same meaning on every marketplace.
ticketTypestringHow the ticket is delivered, e.g. Mobile Transfer.
sellableQuantitiesstringComma-separated quantities you can actually buy, e.g. 2,4.
groupstringThe marketplace's own seating tier, e.g. Upper.
seatsstringComma-separated seat numbers, when exposed by the source.
listingNotesstringSeller notes, e.g. Aisle seat.
dealScorestringThe 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
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=:

MarketplacePathEvent URL domains
Ticketmaster API/v1/capture/ticketmasterticketmaster.com, livenation.com
Vivid Seats API/v1/capture/vividseatsvividseats.com
StubHub API/v1/capture/stubhubstubhub.com
SeatGeek API/v1/capture/seatgeekseatgeek.com
TickPick API/v1/capture/tickpicktickpick.com
Gametime API/v1/capture/gametimegametime.co
Viagogo API/v1/capture/viagogoviagogo.com
GoTickets API/v1/capture/goticketsgotickets.com
Paciolan API/v1/capture/paciolanevenue.net
Country domains and subdomains work too. Each marketplace is matched on its brand name, so 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"
  }
}
StatusCodeMeaning
400missing_urlNo url was given.
400invalid_urlurl is not a valid http(s) URL.
400marketplace_requiredThe URL's host is not one we recognize, so name the marketplace with source=.
400marketplace_mismatchThe source you named and the URL disagree.
401missing_keyNo API key was sent.
401invalid_keyThe key is unknown or revoked.
402no_planNo captures remaining. Choose a plan to continue.
402subscription_inactiveThe subscription lapsed. Update the payment method.
404unknown_marketplaceNo such marketplace. See /v1/capture/sources.
404event_not_foundThe marketplace has no event at that URL. Check the URL: retrying returns the same answer.
429quota_exceededNo captures left on the balance.
429rate_limitedToo many captures in flight, or capacity is saturated. Honour Retry-After.
502capture_failedThe capture could not complete.
503capture_unavailableOur side is down, not your rate. Safe to retry.
504capture_timeoutThe capture did not finish in time. Safe to retry.
Failed captures are never billed. Only a 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.

A 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.
Questions? Reach us at contact@tickets.dev  ·  Back to tickets.dev