Skip to content

Developer docs

Ticker API

Ticker's market data over HTTP: price, inventory and demand for an event, the same readings rolled up by day, a name-to-id search, and performer rank. It authenticates with an API key. There is one Ticker API, and this is its reference.

OpenAPI document, v1

Getting started

Create a key in Settings, under API, then send it as a bearer token against this base URL.

https://api.findticker.com
curl -s \
  -H "Authorization: Bearer $TICKER_API_KEY" \
  "https://api.findticker.com/api/feed/v1/events/ev7k3m9qp2xd/shopping?range=28d&columns=interest,sales"

Responses are JSON. A null means the reading was not observed, never that it was zero. Values are as of computed_at and refresh with each pipeline run. The shopping series is daily.

{
  "event_id": "ev7k3m9qp2xd",
  "current": {
    "interest": 412,
    "sales": 9
  },
  "series": [
    {
      "snapshot_date": "2026-08-31",
      "interest": 412,
      "sales": 9
    }
  ],
  "meta": {
    "range": "28d",
    "coverage_start": "2026-08-04",
    "points": 28,
    "columns": ["interest", "sales"]
  }
}

Authentication

Send the key in the Authorization header. Every secret starts with tk_live_.

Authorization: Bearer tk_live_...

The key reaches the data routes and nothing else. The rest of the Ticker API is session-authenticated, so a key cannot read a watchlist, an alert, or an account, and it cannot create another key.

The account tier is re-read on every request. A downgrade or a cancellation stops the reads at the next call with a 402, without touching the key. A 403 is the other refusal: the tier is there and API access has not been granted.

Ids

Every route takes Ticker ids. An event id is ev plus ten characters, a performer id is pf plus ten characters, and every id the API returns is in that form.

A marketplace id resolves in the same slot when it carries its source prefix, vs:, tm: or tp:. That form does not expire. A bare marketplace id is read as vs: until 2027-09-13 and answers 404 after that date, so nothing you already hold breaks today and nothing stays tied to a marketplace for ever. A whole url segment with its slug is a 400: send the id alone.

Resolve an id by name with the same key:

GET /api/feed/v1/search

q

Required. At least two letters or digits; the % _ and * wildcards are a 400.

limit

How many matches to return, at the published search cap.

curl -s \
  -H "Authorization: Bearer $TICKER_API_KEY" \
  "https://api.findticker.com/api/feed/v1/search?q=morgan%20wallen&limit=5"

Each match carries an event_id, the value every route below takes, and a performer_id for the performer route.

Enumeration

Search resolves a name. It does not page a catalog: there is no list-by-date, list-by-city or list-by-performer route, no cursor on the search response, and a q that is a single character or a wildcard returns 400. Arrive with a name or with ids you already hold.

A screen is the one way to reach events you cannot name, and it is bounded by price rather than by shape: a filter returns matches, and every match returned costs a unit against the same daily budget. Nothing hands back ids for free.

Shopping routes

Ten reads in total, each priced in budget units. The two below are the shopping pair. They answer with current, the latest reading, and series, one point per UTC day. Values are as of computed_at and refresh with each pipeline run. The shopping series is daily. They are the only routes that take columns.

GET /api/feed/v1/events/{eventId}/shopping

1 unit

Shopping demand for one event: the current reading, and one point per UTC day.

eventId

Path. A Ticker event id, `ev` plus ten characters, as returned by the search route. A marketplace id also resolves when it carries its source prefix (`vs:`, `tm:`, `tp:`), and a bare marketplace id resolves until 2027-09-13. An id the catalog does not know returns 404.

range

How far back the daily series reaches: 7d, 14d, 28d, 90d. Defaults to 28d.

include_series

Set to false to return the current reading alone. Defaults to true.

columns

Comma-separated metric names. Omit for both interest and sales.

POST /api/feed/v1/events/shopping

1 unit per event id returned

The same reading for many events in one call.

event_ids

Required array of Ticker event ids, each `ev` plus ten characters. Every id form the single-event routes take works here too, and two spellings of one event are one event. Duplicates are collapsed before the bound is applied, and an id the catalog does not know, or one in a form the API cannot read, comes back under meta.missing rather than failing the call.

range

One of 7d, 14d, 28d, 90d. Defaults to 28d.

include_series

Boolean. Defaults to false on the batch, so a wide read stays cheap.

columns

Array of metric names. Omit for both interest and sales.

A batch carries up to 200 ids. Every batch response echoes the bound in force as meta.batch_max, so a client can size its pages from the answer rather than from this page.

curl -s -X POST "https://api.findticker.com/api/feed/v1/events/shopping" \
  -H "Authorization: Bearer $TICKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event_ids": ["ev7k3m9qp2xd", "ev2p5w8xd4mq"],
    "range": "7d",
    "include_series": true,
    "columns": ["interest", "sales"]
  }'
{
  "data": [ /* one entry per known id, in the order asked for */ ],
  "meta": {
    "requested": 2,
    "returned": 1,
    "missing": ["ev2p5w8xd4mq"],
    "batch_max": 200
  }
}

Columns

columns applies to the two shopping routes above and to no other route on this page. It narrows a shopping response tointerest and sales. An unknown name is a 400 whose message lists the valid set.

A missing key and a null value mean different things. Absent means the request did not select it. Null means it was selected and not observed.

Shopping

interest
current + series
sales
current + series
snapshot_date
series

Both metrics are rolling-window levels, not daily flows. sales is how briskly the event is selling over the window, not a count of tickets sold that day.

Analytics

The analytics pair returns this set on every call. It does not take columns. Values are as of computed_at and refresh with each pipeline run. prediction_markets is present only when at least one Kalshi market maps to the event; the same array appears on the performer route, alongside its raw X counts and Kalshi probability.

event_id
current
computed_at
current
lowest_price_current
current
median_price_current
current
p25_price_current
current
p75_price_current
current
lowest_face_value_current
current
median_face_value_current
current
average_face_value_current
current
highest_face_value_current
current
p25_face_value_current
current
p75_face_value_current
current
listings_current
current
tickets_current
current
percent_remaining
current
interest
current
sales
current
last_price_snapshot_date
current
partial_row
current
prediction_markets
current

Other routes

None of these take columns. The analytics pair answers one flat reading per event, history answers one per day, search answers ids only, and the performer route answers a rank position and its series.

GET /api/feed/v1/events/{eventId}/analytics

1 unit

The current reading for one event: lowest, median, p25 and p75 price, the face-value set, listings, tickets, percent remaining, interest and sales. Values are as of computed_at and refresh with each pipeline run.

eventId

Path. A Ticker event id, `ev` plus ten characters, as returned by the search route. A marketplace id also resolves when it carries its source prefix (`vs:`, `tm:`, `tp:`), and a bare marketplace id resolves until 2027-09-13. An id the catalog does not know returns 404.

POST /api/feed/v1/events/analytics

1 unit per event id returned

The same reading for many events, in the envelope the shopping batch already uses.

event_ids

Required array of Ticker event ids, each `ev` plus ten characters. Every id form the single-event routes take works here too, and two spellings of one event are one event and one unit. Duplicates collapse before the bound applies, and an id the catalog does not know, or one in a form the API cannot read, comes back under meta.missing rather than failing the call. meta.units_charged states what the call cost.

GET /api/feed/v1/events/{eventId}/history

1 unit, whatever days

Price, inventory and demand per day for one event. Values are as of computed_at and refresh with each pipeline run.

eventId

Path. A Ticker event id, `ev` plus ten characters, as returned by the search route. A marketplace id also resolves when it carries its source prefix (`vs:`, `tm:`, `tp:`), and a bare marketplace id resolves until 2027-09-13. An id the catalog does not know returns 404.

days

Defaults to the published cap and stays at that cap: a larger value returns the cap and sets meta.days_capped, never an error.

GET /api/feed/v1/search

1 unit

Resolves a name to the Ticker event ids and performer ids the reading routes accept. Ids only, no readings.

q

Required. At least two letters or digits; the % _ and * wildcards are a 400.

limit

How many matches to return, at the published search cap.

GET /api/feed/v1/performers/{performerId}

1 unit

A performer's current popularity rank, the raw aggregates behind it, and a daily rank series. Rank is published as a position, never as its inputs.

performerId

Path. A Ticker performer id, `pf` plus ten characters, which the search route returns under performers. A marketplace id also resolves with its `vs:` prefix, and bare until 2027-09-13.

Views and screens

These three answer with a set of events rather than a reading for an id. A saved View is one you built in the product and the key reads back its current matches. A screen takes the question in plain language, compiles it with the same model the product's own screener uses, and returns the matching list together with the filter that ran, so nothing about the answer is opaque.

GET /api/feed/v1/views

1 unit

The saved Views on the key's own account, each with the columns its filter reads.

GET /api/feed/v1/views/{viewId}/results

1 unit per event returned

The events one saved View matches right now, with the filter that ran. Values are as of computed_at and refresh with each pipeline run.

viewId

Path. A View id from the Views route. A View on another account returns 404, the same answer as a View that does not exist.

page

Which page of matches to return. Defaults to the first.

limit

Matches per page, at the published page cap. Every match returned costs a unit.

POST /api/feed/v1/screen

1 unit per event returned, plus one assist request

Ask for events in plain language and get the matching list back with the filter the question compiled to. Values are as of computed_at and refresh with each pipeline run.

query

Required. The question in plain language, not a filter. It is compiled by the same model the product's own screener uses, and the compiled filter comes back in the response so you can see what ran.

page

Which page of matches to return. Defaults to the first.

limit

Matches per page, at the published page cap. Omit to let the question decide, within that cap.

Rows carry the analytics reading, the same set the analytics route serves. The compile spends one request from the account's assist allowance and the rows spend budget units, so a question that matches fifty events costs fifty units and one assist request.

curl -s -X POST \
  -H "Authorization: Bearer $TICKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"events under $80 with more than 200 listings"}' \
  "https://api.findticker.com/api/feed/v1/screen"
{
  "query": "events under $80 with more than 200 listings",
  "predicate": {
    "all": [
      { "col": "ea.median_price_current", "op": "<", "val": 80 },
      { "col": "ea.listings_current", "op": ">", "val": 200 }
    ]
  },
  "sort": null,
  "note": null,
  "total": 143,
  "rows": [ { "event_id": "...", "median_price_current": 62, "listings_current": 318 } ],
  "meta": { "page": 1, "limit": 50, "returned": 50, "units_charged": 50 }
}

Rate limits

A shared daily unit budget and a per-minute request budget belong to the account, not to a key, so five keys share one allowance and rotating a key does not buy more calls.

A read costs one unit per event id it returns. A single read costs one. A batch of known ids costs one unit per id returned. Ids the catalog does not know cost nothing and come back under meta.missing. A history read costs one whatever its days value, and a search costs one because it returns ids rather than readings.

Every 200 carries X-Feed-Limit and X-Feed-Remaining. When either budget is exhausted the call returns 429 with Retry-After in seconds: seconds until the minute clears for the burst ceiling, seconds until midnight UTC for the day. Every 200 also carries X-Feed-Cost, the units that call spent.

An admitted request counts on the attempt, so a client that retries an error in a loop is still bounded. A call refused on authentication or entitlement is never admitted, so it costs nothing and carries no X-Feed header at all. These are current limits, not an SLA.

Key lifecycle

Keys live in Settings, under API. An account can hold five at once, which is what makes rotation possible: create the replacement, move your integration over, then revoke the old one.

The secret appears once, in the dialog that creates it. Ticker stores a SHA-256 digest of it and the non-secret prefix, so no route can return the secret a second time and nobody at Ticker can read it back. Copy it into your secret store before you close that dialog. If you lose it, revoke the key and create another.

After creation a key is identified by its prefix, tk_live_ plus eight characters, next to its name, creation date and last use. Revoking is immediate: requests carrying that key return 401 from the next call on.

Treat a key like a password. Keep it server-side, out of source control and out of browser code, and revoke it the moment you think it has leaked.

Errors

Every error is JSON in one shape:

{ "code": 401, "message": "…" }
400

A malformed request, an unknown columns name (the message lists the valid set), a batch over the size bound, or, on a single-id route, an id in a form the API cannot read: a url segment with its slug, or an unknown source prefix. A batch names such an id under meta.missing instead.

401

The Authorization header is missing, malformed, or names a revoked key.

402

The key is valid but its account no longer holds what the API requires, or the screen route ran out of the account's daily assist allowance. The body carries reason.

403

The key is valid and the account holds the tier, but API access has not been granted. Ask us.

404

The read was given an event id the catalog does not know, or a View id that is not on this account. A well-formed id we have never seen is a 404, not a 400.

429

The burst or daily budget is exhausted. Retry-After carries the seconds.

500

A server-side failure. Retry with backoff.

A known event that has no reading yet is not an error. The shopping routes return 200 with current set to null and an empty series; the analytics and history routes return 200 with null readings.