# TM Events API Reference

Base URL: `/api/v1`

Browse this reference in a browser at `/docs`. Raw markdown is at `/docs.md` or `GET /api/v1/docs`.

All authenticated endpoints require an API key via `?apiKey=<key>` query parameter or `x-api-key` header. Price keys are seperate.

---

## All Routes


| Method | Path                              | Description                           | Scopes                              |
| ------ | --------------------------------- | ------------------------------------- | ----------------------------------- |
| `POST` | `/events/`                        | List events with filters              | `events:read`                       |
| `GET`  | `/events/:id`                     | Get single event                      | `events:read`                       |
| `GET`  | `/events/third-party/:id`         | Get third-party URLs                  | `events:read`                       |
| `GET`  | `/events/price/filtered-by-price` | Get events by price range             | `events:read`, `price:read`         |
| `GET`  | `/events/:id/sectioned-pricing`   | Get per-section pricing               | `events:read`, `price:read`         |
| `GET`  | `/events/:id/early-pricing`       | Get early host price tiers            | `events:read`, `price:read`         |
| `GET`  | `/events/:id/early-seats`         | Get configured box-office seat map    | `events:read`, `price:read`         |
| `GET`  | `/events/:id/early-codes`         | Get early promo codes                 | `events:read`, `code:read`          |
| `GET`  | `/events/:id/active-price`        | Get active price range                | `events:read`, `active-price:read`  |
| `POST` | `/events/active-price`            | Get active prices (bulk)              | `events:read`, `active-price:read`  |
| `GET`  | `/events/cheapest-tickets`        | Get cheapest events                   | `events:read`, `active-price:read`  |
| `GET`  | `/events/:id/codes`               | Get presale codes                     | `events:read`, `code:read`          |
| `GET`  | `/events/:id/get-ins`             | Get historical get-in prices          | `events:read`, `get-ins:read`       |
| `GET`  | `/events/:id/seats`               | Get seat counts (dropped vs pending)  | `events:read`, `stock:read`         |
| `GET`  | `/events/:id/seats/all`           | Get all seen seats (priced) for event | `events:read`, `stock:read`         |
| `GET`  | `/events/:id/stock`               | Get full live stock                   | `events:read`, `stock:read`         |
| `GET`  | `/events/:id/limited-stock`       | Get stock by price break              | `events:read`, `limited-stock:read` |
| `POST` | `/sales/`                         | List events by sale date              | `events:read`, `sales:read`         |
| `GET`  | `/sales/event/:eventId`           | Get sales by event                    | `events:read`, `sales:read`         |
| `GET`  | `/artists/:id/genre`              | Get artist genre                      | `events:read`, `artists:read`       |
| `GET`  | `/secondaries/compare/ticketmaster/:id` | Compare TM vs secondary get-in   | `secondaries:read`                  |
| `GET`  | `/secondaries/all/listings`       | Combined marketplace listings         | `secondaries:read`                  |
| `GET`  | `/secondaries/stubhub/listings/:id` | StubHub listings                    | `secondaries:read`                  |
| `GET`  | `/secondaries/vividseats/listings/:id` | Vivid Seats listings             | `secondaries:read`                  |
| `GET`  | `/secondaries/gametime/listings/:id` | Gametime listings                  | `secondaries:read`                  |
| `GET`  | `/secondaries/seatgeek/listings/:id` | SeatGeek listings                  | `secondaries:read`                  |


---

## Events

Base path: `/api/v1/events`

All event routes pass through logging and rate-limit middleware.

---

### `POST /`

List events with filters.

**Auth:** None (public)

**Request Body:**

```json
{
  "filters": {
    "eventStartDate": "2025-06-01",
    "eventEndDate": "2025-12-31",
    "status": "onsale",
    "sort": "date",
    "order": "desc",
    "page": 0,
    "limit": 25,
    "onlyTmEvents": false,
    "vividStrict": false,
    "excludePastEvents": true,
    "tmStrict": false,
    "maxInventory": 0
  }
}
```


| Field               | Type                | Description                                      |
| ------------------- | ------------------- | ------------------------------------------------ |
| `eventStartDate`    | string (YYYY-MM-DD) | Start of date range (default: today)             |
| `eventEndDate`      | string (YYYY-MM-DD) | End of date range (default: 1 year from start)   |
| `status`            | string              | Filter by event status                           |
| `sort`              | string              | `"date"` or `"status"`                           |
| `order`             | string              | `"asc"` or `"desc"`                              |
| `page`              | int                 | Page number (0-indexed)                          |
| `limit`             | int                 | Results per page (min 10)                        |
| `onlyTmEvents`      | bool                | Only include events with a TM ID                 |
| `vividStrict`       | bool                | Only include events with a Vivid Seats ID        |
| `excludePastEvents` | bool                | Exclude events that have already started         |
| `tmStrict`          | bool                | Only include events with updated seat inventory  |
| `maxInventory`      | int                 | Max available seats filter (requires `tmStrict`) |


**Response (200):**

```json
{
  "data": {
    "events": [
      {
        "tm_id": "abc123",
        "name": "Event Name",
        "start_time": "2025-07-01T20:00:00Z",
        "end_time": "2025-07-01T23:00:00Z",
        "status": "onsale",
        "url": "https://www.ticketmaster.com/...",
        "image_url": "https://...",
        "currency": "USD",
        "category": "Music",
        "genre": "Rock",
        "venue": {
          "name": "Madison Square Garden",
          "capacity": 20000,
          "street": "4 Pennsylvania Plaza",
          "city": "New York",
          "state": "NY",
          "country": "US"
        },
        "price": {
          "min_price": 49.99,
          "max_price": 299.99,
          "currency": "USD"
        },
        "sales": {
          "public": {
            "start_date_time": "2025-05-01T10:00:00Z",
            "end_date_time": "2025-07-01T18:00:00Z",
            "start_tbd": false
          },
          "presales": [
            {
              "name": "Artist Presale",
              "description": "Exclusive artist presale",
              "start_date_time": "2025-04-28T10:00:00Z",
              "end_date_time": "2025-04-30T22:00:00Z",
              "url": "https://...",
              "visible": true
            }
          ]
        },
        "details": {
          "stubhub": {
            "url": "https://...",
            "relevent_id": "...",
            "low_price": 55.0,
            "high_price": 500.0,
            "total_listings": 120,
            "total_seats": 350
          },
          "vivid_seats": { "..." : "..." },
          "seat_geek": { "..." : "..." },
          "game_time": { "..." : "..." },
          "automatiq": { "..." : "..." }
        },
        "attraction": {
          "name": "Artist Name",
          "type": "Music",
          "upcoming_events": 15,
          "url": "https://...",
          "seo_name": "artist-name",
          "league": ""
        }
      }
    ]
  },
  "total": 1500,
  "count": 25,
  "page": 0,
  "total_pages": 60,
  "pages_remaining": 59,
  "limit": 25
}
```

---

### `GET /:id`

Get a single event by TM ID.

**Auth:** `events:read`

**Params:**

- `id` (path) -- Ticketmaster event ID
- `getSales` (query, optional) -- `"true"` to include Vivid/StubHub sales data

**Response (200):**

```json
{
  "event": { "...EventData..." }
}
```

**Errors:**

- `400` -- Invalid event ID
- `404` -- Event not found

---

### `GET /third-party/:id`

Get third-party marketplace URLs for an event.

**Auth:** None

**Response (200):**

```json
{
  "name": "Event Name",
  "ticketmaster": "https://www.ticketmaster.com/...",
  "vivid": "https://www.vividseats.com/...",
  "stubhub": "https://www.stubhub.com/...",
  "seatGeek": "https://www.seatgeek.com/..."
}
```

---

### `GET /price/filtered-by-price`

Get events filtered by price range.

**Auth:** `price:read`

**Query Params:**

- `minPrice` (required) -- Minimum price
- `maxPrice` (optional, default: `999999999`) -- Maximum price
- `countryCode` (optional, default: `"US"`) -- Comma-separated country codes

**Response (200):**

```json
{
  "events": [ "...EventData[]..." ],
  "total": 42
}
```

---

### `GET /:id/sectioned-pricing`

Get per-section min/max pricing for an event (live from websocket monitor).

**Auth:** `events:read`, `price:read`

**Response (200):**

```json
{
  "ticketMapper": {
    "ORCH1": {
      "section": "ORCH1",
      "minPrice": 89.50,
      "maxPrice": 350.00
    },
    "MEZZ2": {
      "section": "MEZZ2",
      "minPrice": 45.00,
      "maxPrice": 120.00
    }
  }
}
```

---

### `GET /:id/early-pricing`

Get the host price scale for an event as soon as the venue box office loads it,
often before the public pricing buffer has anything.

**Auth:** `events:read`, `price:read`

**Query:**

| Param | Default | Description |
| ----- | ------- | ----------- |
| `availability` | `true` | Include live seat counts per tier. |
| `allTicketTypes` | `false` | Include senior/student/group variants; default is base adult only. |

**Response (200):**

```json
{
  "eventId": "150064F581713D8B",
  "currency": "USD",
  "minPrice": 73.75,
  "maxPrice": 133.75,
  "totalAvailableSeats": 842,
  "pricing": [
    {
      "faceValue": 129.5,
      "fees": 4.25,
      "total": 133.75,
      "availableSeats": 120,
      "maximumContiguousSeats": 8
    }
  ]
}
```

**Errors:**

- `404` -- Early pricing is unavailable for this event

---

### `GET /:id/early-seats`

Get the configured section, row, and seat ranges joined to their preloaded
price codes. This seat map is often available before public onsale and is not
live inventory — a configured range can still be held back when the sale opens.

**Auth:** `events:read`, `price:read`

`totalSeats` is the venue summary when present, otherwise the sum of parsed
ranges. `priceMappedSeatCount` is seats that joined to at least one price
code; `unpricedSeatCount` is the remainder. Ranges with no matching price
code omit `pricing`. All money fields are major currency units.

**Response (200):**

```json
{
  "eventId": "150064F581713D8B",
  "currency": "USD",
  "totalSeats": 1840,
  "sectionCount": 12,
  "seatRangeCount": 86,
  "priceMappedSeatCount": 1620,
  "unpricedSeatCount": 220,
  "sections": [
    {
      "name": "101",
      "description": "Lower Bowl",
      "type": "Standard",
      "generalAdmission": false,
      "seatCount": 48,
      "ranges": [
        {
          "row": "A",
          "firstSeat": "1",
          "lastSeat": "8",
          "seatCount": 8,
          "seatIncrement": 1,
          "pricing": [
            {
              "description": "Adult",
              "faceValue": 129.5,
              "fees": 4.25,
              "tax": 0,
              "serviceCharge": 0,
              "total": 133.75
            }
          ]
        },
        {
          "row": "B",
          "firstSeat": "1",
          "lastSeat": "8",
          "seatCount": 8,
          "seatIncrement": 1
        }
      ]
    }
  ]
}
```

Responses are cached for 2 minutes (`Cache-Control: public, max-age=…`).

**Errors:**

- `400` -- Missing event ID
- `404` -- Early seats are unavailable for this event

---

### `GET /:id/early-codes`

Get early promo codes for an event.

**Auth:** `events:read`, `code:read`

**Response (200):**

```json
{
  "eventId": "abc123",
  "codes": ["PRESALE"]
}
```

An event with no matching code returns an empty `codes` array.

**Errors:**

- `404` -- Early codes are unavailable for this event

---

### `GET /:id/active-price`

Get the active (real-time) price range for an event.

**Auth:** `events:read`, `active-price:read`

**Response (200):**

```json
{
  "eventId": "abc123",
  "prices": {
    "minPrice": 29.99,
    "maxPrice": 499.99,
    "allInMinPrice": 45.00,
    "allInMaxPrice": 525.00,
    "allInMinPriceFiltered": 50.00,
    "allInMaxPriceFiltered": 510.00,
    "facilityFee": 5.50,
    "updatedAt": "2025-06-15T14:30:00Z"
  }
}
```

---

### `POST /active-price`

Get active prices for multiple events at once.

**Auth:** `active-price:read`

**Request Body:**

```json
{
  "event_ids": ["abc123", "def456", "ghi789"]
}
```

**Response (200):**

```json
{
  "data": [
    {
      "eventId": "abc123",
      "prices": {
        "minPrice": 29.99,
        "maxPrice": 499.99,
        "allInMinPrice": 45.00,
        "allInMaxPrice": 525.00,
        "allInMinPriceFiltered": 50.00,
        "allInMaxPriceFiltered": 510.00,
        "facilityFee": 5.50,
        "updatedAt": "2025-06-15T14:30:00Z"
      }
    }
  ]
}
```

---

### `GET /cheapest-tickets`

Get cheapest active-priced events within a date range.

**Auth:** `active-price:read`

**Query Params:**

- `limit` (optional, default: `25`, max: `300`)
- `startDate` (optional, RFC3339, default: yesterday)
- `endDate` (optional, RFC3339, default: tomorrow)
- `maxPrice` (optional) -- Max all-in min price filter

**Response (200):**

```json
{
  "data": [ "...ActivePriceResponse[]..." ]
}
```

---

### `GET /:id/codes`

Get presale/offer codes for an event (live from websocket monitor + database).

**Auth:** `events:read`, `code:read`

**Response (200):**

```json
[
  {
    "ID": "...",
    "EventID": "abc123",
    "Code": "510002",
    "Name": "Mastercard presales"
  }
]
```

---

### `GET /:id/get-ins`

Get historical "get-in" prices for an event.

**Auth:** `events:read`, `get-ins:read`

**Response (200):**

```json
{
  "eventId": "abc123",
  "prices": [
    {
      "price": 55.00,
      "market": "primary",
      "currency": "USD",
      "scrapedAt": "2025-06-10T12:00:00Z"
    }
  ]
}
```

---

### `GET /:id/seats`

Get seat counts for an event, split by priced vs unpriced.

**Auth:** `events:read`, `stock:read`

**Response (200):**

```json
{
  "event_id": "abc123",
  "total": 5000,
  "seen": 3200,
  "pending": 1800
}
```

---

### `GET /:id/seats/all`

Dump every "seen" seat for an event (rows where `list_price > 0`). No pagination — returns the full set.

**Auth:** `events:read`, `stock:read`

**Response (200):**

```json
{
  "event_id": "abc123",
  "count": 3200,
  "seats": [
    {
      "Section": "111A",
      "Row": "29",
      "SeatNumber": 14,
      "Name": "POTENT",
      "Description": "Artist Presale",
      "Password": true,
      "ListPrice": 119.5,
      "FaceValue": 115,
      "FacilityFees": 4.5,
      "ServiceCharge": 30.45,
      "FirstSeenAt": "2025-06-10T12:00:00Z",
      "LastUpdatedAt": "2025-06-15T14:30:00Z"
    }
  ]
}
```

---

### `GET /:id/stock`

Get full live stock (all available tickets) for an event.

**Auth:** `events:read`, `stock:read`

A numeric `:id` is a SeatGeek event id (SeatGeek-primary). Those return this
same shape from SeatGeek box-office inventory (`open` and `mercury` markets)
and are not looked up as Ticketmaster events. `codes` is then SeatGeek's
price types, keyed by price type id, with `code` the price type label. `map`
is empty. A stored event whose URL is `seatgeek.com`, or that is neither
Ticketmaster nor AXS and has a SeatGeek id, takes the same path. Every other
event still comes from the Ticketmaster feed.

**Query Params:**

- `did` (optional, default: `"default"`) -- Display ID

**Response (200):**

```json
{
  "eventId": "abc123",
  "availableSeats": 1500,
  "platinumSeats": 75,
  "map": "https://mapsapi.tmol.io/maps/geometry/3/event/abc123/image?...",
  "bySection": {
    "ORCH1": 120,
    "MEZZ2": 85
  },
  "codes": {
    "Mastercard Presale": {
      "code": "Mastercard Presale",
      "description": "...",
      "quantity": 50
    }
  },
  "stock": [
    {
      "name": "Standard Ticket",
      "section": "ORCH1",
      "row": "A",
      "seat": "12",
      "listPrice": 150.00,
      "faceValue": 125.00,
      "facilityFee": 10.00,
      "serviceCharge": 15.00,
      "password": false
    }
  ],
  "consecutivePairs": 25,
  "pairs": [
    [
      { "section": "ORCH1", "row": "A", "seat": "12", "..." : "..." },
      { "section": "ORCH1", "row": "A", "seat": "13", "..." : "..." }
    ]
  ]
}
```

**Errors:**

- `404` -- No stock available

---

### `GET /:id/limited-stock`

Get stock summarized by price break (no individual ticket listing).

**Auth:** `limited-stock:read`

**Query Params:**

- `did` (optional, default: `"default"`)

**Response (200):**

```json
{
  "eventId": "abc123",
  "availableSeats": 1500,
  "priceBreaks": [
    {
      "listPrice": 150.00,
      "faceValue": 125.00,
      "facilityFee": 10.00,
      "serviceCharge": 15.00,
      "quantity": 200
    },
    {
      "listPrice": 85.00,
      "faceValue": 70.00,
      "facilityFee": 5.00,
      "serviceCharge": 10.00,
      "quantity": 450
    }
  ]
}
```

---

## Sales

Base path: `/api/v1/sales`

**Auth (all routes):** `sales:read`

---

### `POST /`

List events by sale date range (presale/public sale start dates).

**Request Body:**

```json
{
  "filters": {
    "eventId": "abc123",
    "saleStartDate": "2025-06-01",
    "saleEndDate": "2025-06-30",
    "status": "onsale",
    "sort": "date",
    "order": "desc",
    "page": 0,
    "limit": 25
  }
}
```

**Response (200):**

```json
{
  "data": {
    "events": [ "...EventData[]..." ]
  },
  "total": 100,
  "count": 25,
  "page": 0,
  "total_pages": 4,
  "pages_remaining": 3,
  "limit": 25
}
```

---

### `GET /event/:eventId`

Get sales by event ID.

**Response (200):**

```json
{
  "message": "Get sales by event",
  "eventId": "abc123"
}
```

---

## Artists

Base path: `/api/v1/artists`

**Auth (all routes):** `artists:read`

---

### `GET /:id/genre`

Get genre classification for an artist by legacy ID.

**Response (200):**

```json
{
  "artist_id": "K8vZ917...",
  "artist_name": "Artist Name",
  "genre": "Rock",
  "sub_genre": "Alternative",
  "sub_type": "Band",
  "segment": "Music",
  "url": "https://..."
}
```

---

## Secondaries

Base path: `/api/v1/secondaries`

**Auth (all routes):** `secondaries:read`

All secondaries routes pass through logging and rate-limit middleware.

Listings are normalized into a shared shape. Sections are grouped into **zones**
(`100s`, `200s`, …; sections under 100 become `Floor`; named areas like `GA`
stay as-is). `getIn` is the cheapest listing price; `highTicket` is the most
expensive.

Single-marketplace listing endpoints populate `getIn`, `highTicket`, and
`breakdown`. `GET /all/listings` and `GET /compare/ticketmaster/:id` merge
listings across sources and do not aggregate those top-level fields.

---

### `GET /compare/ticketmaster/:id`

Compare Ticketmaster dropped-seat (primary) get-in prices per zone against
StubHub, Vivid Seats, Gametime, and SeatGeek get-in prices for the same event.

`:id` is a Ticketmaster event ID. Linked marketplace IDs are resolved from the
event record (`stubhub`, `vividseats`, `gametime`, and `seatgeek` when present).
A marketplace with no linked ID is skipped.

`percentChange` is the change from the primary get-in to the secondary get-in:
positive means the secondary market is more expensive (markup); negative means
it is cheaper than the dropped-seat price.

**Auth:** `secondaries:read`

**Params:**

- `id` (path) -- Ticketmaster event ID

**Response (200):**

```json
{
  "total": 4200,
  "totalListings": 980,
  "getIn": 0,
  "highTicket": 0,
  "eventIds": {
    "ticketmaster": "abc123",
    "stubhub": "104512345",
    "vividseats": "3845123",
    "seatgeek": "6123456",
    "gametime": "gt_abc123"
  },
  "comparison": [
    {
      "zone": "100s",
      "primaryGetIn": 89.50,
      "primaryPriceBreaks": [
        { "price": 89.50, "seats": 24 },
        { "price": 125.00, "seats": 18 }
      ],
      "marketplaces": [
        {
          "marketplace": "gametime",
          "getIn": 112.00,
          "difference": 22.50,
          "percentChange": 25.14
        },
        {
          "marketplace": "stubhub",
          "getIn": 145.00,
          "difference": 55.50,
          "percentChange": 62.01
        },
        {
          "marketplace": "vividseats",
          "getIn": 138.00,
          "difference": 48.50,
          "percentChange": 54.19
        }
      ]
    }
  ],
  "listings": [
    {
      "marketplace": "stubhub",
      "section": "111",
      "row": "12",
      "qty": 2,
      "price": 145.00,
      "formattedPrice": "$145.00"
    },
    {
      "marketplace": "ticketmaster",
      "section": "111A",
      "row": "14",
      "qty": 1,
      "price": 89.50,
      "formattedPrice": "$89.50"
    }
  ]
}
```

Primary listings use all-in price (`faceValue + facilityFees + serviceCharge`).
`primaryPriceBreaks` only count Ticketmaster seats that are currently in stock.

**Errors:**

- `400` -- Missing event ID
- `500` -- Failed to load the event, dropped seats, or marketplace listings

---

### `GET /all/listings`

Fetch listings from multiple marketplaces in one request and return them as a
single combined list.

At least one of `vividId`, `stubhubId`, `gametimeId`, or `seatgeekId` is
required. Each marketplace is fetched only when its id is provided.

**Auth:** `secondaries:read`

**Query Params:**

- `vividId` (optional) -- Vivid Seats event ID
- `stubhubId` (optional) -- StubHub event ID
- `gametimeId` (optional) -- Gametime event ID
- `seatgeekId` (optional) -- SeatGeek event ID
- `zoneId` (optional) -- StubHub zone/selection filter (`selection`). Ignored
  for Vivid Seats, Gametime, and SeatGeek.

**Response (200):**

```json
{
  "total": 850,
  "totalListings": 210,
  "getIn": 0,
  "highTicket": 0,
  "listings": [
    {
      "marketplace": "vividseats",
      "section": "111",
      "row": "8",
      "qty": 2,
      "price": 138.00,
      "formattedPrice": "138.00"
    },
    {
      "marketplace": "stubhub",
      "section": "111",
      "row": "12",
      "qty": 2,
      "price": 145.00,
      "formattedPrice": "$145.00"
    },
    {
      "marketplace": "gametime",
      "section": "112",
      "row": "4",
      "qty": 3,
      "price": 112.00,
      "formattedPrice": "$112.00"
    }
  ]
}
```

A marketplace that fails to fetch is omitted from the result (the others still
return). `breakdown` is not populated on this endpoint.

**Errors:**

- `400` -- No marketplace event ID was provided

---

### `GET /stubhub/listings/:id`

Get StubHub listings for an event, with zone-level breakdown.

**Auth:** `secondaries:read`

**Params:**

- `id` (path) -- StubHub event ID
- `zoneId` (query, optional) -- Zone/selection filter (`selection`)

**Response (200):**

```json
{
  "total": 420,
  "totalListings": 95,
  "getIn": 89.00,
  "highTicket": 650.00,
  "breakdown": {
    "stubhub": {
      "count": 420,
      "getIn": 89.00,
      "highTicket": 650.00,
      "zones": {
        "100s": {
          "seats": 180,
          "listings": 40,
          "getIn": 145.00,
          "highTicket": 425.00
        },
        "200s": {
          "seats": 240,
          "listings": 55,
          "getIn": 89.00,
          "highTicket": 650.00
        }
      }
    }
  },
  "listings": [
    {
      "marketplace": "stubhub",
      "section": "111",
      "row": "12",
      "qty": 2,
      "price": 145.00,
      "formattedPrice": "$145.00"
    }
  ]
}
```

**Errors:**

- `400` -- Missing event ID
- `500` -- Failed to get listings

---

### `GET /vividseats/listings/:id`

Get Vivid Seats listings for an event, with zone-level breakdown.

Prices are all-in (`aip`).

**Auth:** `secondaries:read`

**Params:**

- `id` (path) -- Vivid Seats event ID

**Response (200):**

```json
{
  "total": 310,
  "totalListings": 72,
  "getIn": 92.00,
  "highTicket": 480.00,
  "breakdown": {
    "vividseats": {
      "count": 310,
      "getIn": 92.00,
      "highTicket": 480.00,
      "zones": {
        "100s": {
          "seats": 140,
          "listings": 30,
          "getIn": 138.00,
          "highTicket": 380.00
        }
      }
    }
  },
  "listings": [
    {
      "marketplace": "vividseats",
      "section": "111",
      "row": "8",
      "qty": 2,
      "price": 138.00,
      "formattedPrice": "138.00"
    }
  ]
}
```

**Errors:**

- `400` -- Missing event ID
- `500` -- Failed to get listings

---

### `GET /gametime/listings/:id`

Get Gametime listings for an event, with zone-level breakdown.

Prices are all-in totals (cents converted to dollars).

**Auth:** `secondaries:read`

**Params:**

- `id` (path) -- Gametime event ID

**Response (200):**

```json
{
  "total": 120,
  "totalListings": 43,
  "getIn": 78.00,
  "highTicket": 320.00,
  "breakdown": {
    "gametime": {
      "count": 120,
      "getIn": 78.00,
      "highTicket": 320.00,
      "zones": {
        "Floor": {
          "seats": 48,
          "listings": 16,
          "getIn": 210.00,
          "highTicket": 320.00
        },
        "100s": {
          "seats": 72,
          "listings": 27,
          "getIn": 78.00,
          "highTicket": 195.00
        }
      }
    }
  },
  "listings": [
    {
      "marketplace": "gametime",
      "section": "112",
      "row": "4",
      "qty": 3,
      "price": 112.00,
      "formattedPrice": "$112.00"
    }
  ]
}
```

**Errors:**

- `400` -- Missing event ID
- `500` -- Failed to get listings

---

### `GET /seatgeek/listings/:id`

Get SeatGeek listings for an event, with zone-level breakdown.

`:id` is a SeatGeek numeric event ID. Every seller channel is included (box
office and resale). `price` is the all-in price per ticket and `qty` is the
listing quantity.

SeatGeek sits behind DataDome; challenges are solved transparently. A cold
call takes a couple of seconds, and later calls for the same event reuse the
session.

**Auth:** `secondaries:read`

**Params:**

- `id` (path) -- SeatGeek event ID

**Response (200):**

```json
{
  "total": 3961,
  "totalListings": 1064,
  "getIn": 89.00,
  "highTicket": 1494.72,
  "breakdown": {
    "seatgeek": {
      "count": 3961,
      "getIn": 89.00,
      "highTicket": 1494.72,
      "zones": {
        "100s": {
          "seats": 2140,
          "listings": 612,
          "getIn": 89.00,
          "highTicket": 1494.72
        }
      }
    }
  },
  "listings": [
    {
      "marketplace": "seatgeek",
      "section": "105",
      "row": "1",
      "qty": 2,
      "price": 1494.72,
      "formattedPrice": "$1494.72"
    }
  ]
}
```

**Errors:**

- `400` -- Missing event ID
- `404` -- Event not found
- `500` -- Failed to get listings

---

## Available Auth Scopes


| Scope                | Description                       | Includes                    |
| -------------------- | --------------------------------- | --------------------------- |
| `events:read`        | Read event data                   | —                           |
| `price:read`         | Read pricing data                 | `events:read`               |
| `active-price:read`  | Read active/real-time prices      | `events:read`               |
| `stock:read`         | Read live stock data              | `events:read`               |
| `limited-stock:read` | Read limited stock (price breaks) | `events:read`, `stock:read` |
| `code:read`          | Read presale codes                | `events:read`               |
| `get-ins:read`       | Read get-in prices                | `events:read`               |
| `sales:read`         | Read sales data                   | `events:read`               |
| `artists:read`       | Read artist data                  | `events:read`               |
| `secondaries:read`   | Read secondary-market listings    | `events:read`               |


> The `Includes` column lists scopes automatically granted when this scope is subscribed. For example, subscribing to `active-price:read` also grants `events:read` and `price:read` at no additional cost — you only pay the `active-price:read` tier price.

---

## Scope Pricing

Each scope is priced individually based on the customer's monthly request volume for endpoints covered by that scope. Prices below are in USD per month and represent the **flat monthly cost** for the included request quota in that tier. Subscribing to any scope automatically grants the bundled scopes listed in the [Available Auth Scopes](#available-auth-scopes) table (e.g. every paid scope includes `events:read`).

> Tiers are billed monthly. A customer subscribes to one tier per scope. Combined / bundled pricing is negotiated separately for Enterprise.


| Scope                | Tier 1 — up to 1K req/mo | Tier 2 — up to 10K req/mo | Tier 3 — up to 50K req/mo | Enterprise (100K+ req/mo) |
| -------------------- | ------------------------ | ------------------------- | ------------------------- | ------------------------- |
| `events:read`        | $200                     | $500                      | $1,000                    | Contact sales             |
| `price:read`         | $TBD                     | $TBD                      | $TBD                      | Contact sales             |
| `active-price:read`  | $300                     | $750                      | $1,250                    | Contact sales             |
| `stock:read`         | $400                     | $900                      | $2,000                    | Contact sales             |
| `limited-stock:read` | $450                     | $1000                     | $2,200                    | Contact sales             |
| `code:read`          | $300                     | $750                      | $1,250                    | Contact sales             |
| `get-ins:read`       | $300                     | $750                      | $1,250                    | Contact sales             |
| `sales:read`         | $300                     | $750                      | $1,250                    | Contact sales             |
| `artists:read`       | $220                     | $525                      | $1,050                    | Contact sales             |
| `secondaries:read`   | $TBD                     | $TBD                      | $TBD                      | Contact sales             |


### Billing Notes

- A "request" is any successful (`2xx`) or client-error (`4xx`, excluding `429`) response on an endpoint that requires the given scope.
- `429` (rate-limited) and `5xx` responses are not billed.
- If an endpoint requires multiple scopes (e.g. `/events/:id/sectioned-pricing` requires `events:read` + `price:read`), the request counts against **each** subscribed scope's quota.
- Unused quota does not roll over month to month.
- Exceeding a tier's request quota requires upgrading to the next tier for the following billing period.

---

## Common Error Responses

All error responses follow this format:

```json
{
  "error": "Description of the error"
}
```


| Status | Meaning                                   |
| ------ | ----------------------------------------- |
| `400`  | Bad request / missing required parameters |
| `401`  | Missing or invalid API key                |
| `403`  | Insufficient permissions (wrong scopes)   |
| `404`  | Resource not found                        |
| `429`  | Rate limit exceeded                       |
| `500`  | Internal server error                     |


### Rate Limit Error (429):

```json
{
  "error": "Rate limit exceeded",
  "retry_after": 1200.5,
  "reset_time": "2025-06-15T15:30:00Z",
  "rate_limit": {
    "requests_per_window": 100,
    "window_duration_seconds": 3600
  }
}
```

Rate limit headers are included on all authenticated responses:

- `X-RateLimit-Limit` -- Max requests per window
- `X-RateLimit-Remaining` -- Remaining requests
- `X-RateLimit-Reset` -- Unix timestamp when window resets

