Skip to main content

Prediction Market Event Reference Data

Overview​

Prediction market event reference data describes every prediction market event that Coin Metrics collects from Kalshi and Polymarket. An event is the question a venue lists, such as "Fed decision in Dec 2027?", and it groups one or more tradable markets, each a Yes or No contract on one outcome or variant of that question. For each event the data provides its title and description, the recurring series it belongs to, its category and tags, its lifecycle status and close time, and whether its markets are mutually exclusive. It is used to discover and classify prediction markets, to group individual markets under the question they answer, and to build the list of markets to query for trades, candles, and open interest.

At a Glance​

Data typeEntitiesFrequency / cadenceUnitPrimary endpoint
Prediction market event reference dataPrediction market events (Kalshi, Polymarket)Reference data, refreshed on an ongoing basis as venues changeNot applicable (descriptive metadata)/reference-data/events

Schema​

The response returns one object per event. Every event carries the four identification fields. The remaining fields are populated only where the venue provides them, so Kalshi and Polymarket events each return a different subset of the schema below. Fields without a value are omitted from the response.

Identification (all events)​

FieldTypeDescriptionNotes
eventstringGlobally unique Coin Metrics identifier for the event, of the form <exchange>-<event_ticker>. See Event identifiers.Required
exchangestringCoin Metrics identifier for the venue that defines the event. Currently kalshi or polymarket.Required
event_tickerstringEvent identifier exactly as reported by the venue: the event ticker on Kalshi, the event slug on Polymarket.Required
last_updated_timestring (date-time)Time at which Coin Metrics last observed a change to this event's metadata. See Change tracking.Required

Description and classification​

FieldTypeDescriptionNotes
titlestringPrimary human-readable title of the event.All venues
subtitlestringSecondary human-readable title supplied by the venue.Kalshi
descriptionstringLong-form description of the event supplied by the venue, typically including its resolution rules.Polymarket
categorystringBroad subject category assigned to the event by the venue.Kalshi, Polymarket (rarely)
subcategorystringMore specific venue-provided classification within the category.Polymarket (rarely)
tagsarray[object]Venue-provided labels used to classify, group, or discover the event, passed through verbatim from the venue. Each tag carries at least an id, a slug, and a label.Polymarket

Series​

FieldTypeDescriptionNotes
series_tickerstringVenue-native identifier of the recurring series or collection the event belongs to.All venues
series_titlestringHuman-readable title of the series.All venues
series_frequencystringVenue-provided recurrence frequency of the series, for example daily, weekly, or custom. Values are venue-specific.All venues

Lifecycle​

FieldTypeDescriptionNotes
statusstringLifecycle status of the event. active, inactive, or closed on Kalshi, and active, closed, or archived on Polymarket. See Status and close time.All venues
created_timestring (date-time)Time at which the venue created the event.Polymarket
close_timestring (date-time)Time at which the event closes, or closed. Determines whether the event is live. See Status and close time.All venues

Outcome structure and venue terms​

FieldTypeDescriptionNotes
mutually_exclusivebooleanWhether the event's markets represent mutually exclusive outcomes. See Mutually exclusive outcomes and negative risk.Kalshi (true or false), Polymarket (true only)
neg_risk_enabledbooleanPolymarket flag indicating that the event uses the negative-risk mechanism.Polymarket
neg_risk_augmentedbooleanPolymarket flag indicating that the negative-risk event supports adding outcomes after creation.Polymarket
strike_datestring (date-time)Date and time used by the event's strike definition.Kalshi
strike_periodstringTime period used by the event's strike definition, for events defined over a period rather than a date.Kalshi (rarely)
collateral_return_typestringVenue-reported treatment of collateral returns for the event, for example MECNET or DIRECNET.Kalshi
info

Conventions. Timestamps (last_updated_time, created_time, close_time, and strike_date) are UTC ISO-8601 with nanosecond resolution. Fields without a value are omitted rather than returned as null. Text and classification values (status, category, series_frequency, and the contents of tags) are passed through in each venue's own vocabulary and are not harmonized across venues. Objects inside tags keep the venue's own field names and timestamp formats.

Methodology​

Collection​

Coin Metrics collects event metadata directly from the public API of each supported prediction market venue, currently Kalshi and Polymarket. Each venue is polled about every five minutes. Every poll picks up events that were created or changed since the previous one and also advances a slower crawl through the venue's full catalog, so that older and already settled events are backfilled over time. Events and their markets are collected in the same pass, which keeps this endpoint consistent with the prediction markets served by /reference-data/markets. Records are never deleted: closed, settled, and archived events remain available with include_expired=true.

On Kalshi, category is taken from the event and falls back to the category of its series, while series_title and series_frequency are attached from the venue's series catalog. On Polymarket, the series fields come from the first series the venue lists on the event. Kalshi multivariate (combination) events, which bundle markets from several other events, are not collected.

Event identifiers​

Every event is identified by event, built as <exchange>-<event_ticker>. The event_ticker keeps the venue's own identifier verbatim: the event ticker on Kalshi (for example KXFEDDECISION-27DEC) and the event slug on Polymarket (for example largest-company-end-of-december-2026). Tickers are case-sensitive and are matched exactly, while the exchange prefix is not case-sensitive. Because a ticker can itself contain hyphens, split an event value on its first hyphen only. The same identifier appears in the event field of each prediction market in /reference-data/markets, which is how events and markets join.

Status and close time​

The status and close_time fields come from the venue where it provides them, and are otherwise derived from the event's markets.

  • Polymarket. status is archived if the venue has archived the event, closed if the venue has closed it, and active otherwise. Once the event has closed, close_time is the time the venue closed it. While it is open, close_time is the latest close or scheduled end among its markets.
  • Kalshi. The venue does not report an event-level status or close time, so both are derived from the event's markets. An event is active if at least one of its markets is open for trading, inactive if none is open but at least one has not yet closed (for example a market that has not opened yet), and closed once every market has closed (or when none of its markets has been collected). close_time is the latest scheduled close among its markets, falling back to their latest possible expiration.

Derived values are recomputed each time the event itself is collected again, so they can briefly trail a change to one of its markets.

Liveness and expired events​

An event is live while its close_time is in the future. A request that lists all events, or filters by exchange, returns only live events by default. Set include_expired=true to also return events whose close_time has passed or is unknown. A request that names specific events with events or event_tickers returns them whatever their status, so a closed event can always be looked up by its identifier.

Markets apply the same idea to their own expiration in /reference-data/markets (see Liveness and expired markets). Because events and markets use different timestamps, they do not always agree. Kalshi markets, for example, stay in the default market listing for about a week after they settle, so the markets of a closed event can still appear there.

Liveness also does not follow status. An event whose close_time has passed drops out of the default listing even while its status is still active. This is common on Polymarket, where an event can stay active after its scheduled end while it awaits resolution.

Mutually exclusive outcomes and negative risk​

An event groups one or more binary markets. The mutually_exclusive field records whether those markets are alternatives of which only one can resolve Yes, such as the possible outcomes of a single rate decision. On Kalshi the venue reports the value as true or false. On Polymarket it is derived from the venue's negative-risk designation, which Polymarket applies to events whose markets are mutually exclusive. It is true for those events and omitted otherwise, so its absence on a Polymarket event does not mean the outcomes overlap.

The neg_risk_enabled and neg_risk_augmented fields pass through Polymarket's own flags. The first marks events set up for the negative-risk mechanism. The second marks events to which new outcomes can be added after creation (for example a new candidate entering a race).

Change tracking​

The last_updated_time field is the time Coin Metrics last observed a change to any of the event's stored fields. It is not the venue's own update time. Collecting an event again without a change leaves it untouched, so comparing last_updated_time across two pulls shows which events changed in between. A known created_time is never overwritten by an empty value on a later collection.

Accessing the Data​

Prediction market event metadata is served by the /reference-data/events endpoint. Requests are filtered by one of events (Coin Metrics event identifiers), exchange (kalshi or polymarket), or event_tickers (venue-native tickers, matched across all venues). Only one of the three can be used per request. Add include_expired=true to an unfiltered or exchange request to include expired events. Results are limited to the venues your API key has access to: naming a venue your key does not cover returns an error, while an unfiltered request simply omits it.

Python Client
from coinmetrics.api_client import CoinMetricsClient

client = CoinMetricsClient("YOUR_API_KEY")

# Metadata for specific events, live or expired
events = client.reference_data_events(
events=["kalshi-KXFEDDECISION-27DEC", "polymarket-largest-company-end-of-december-2026"],
).to_dataframe()
print(events)

# All live events on a venue
kalshi = client.reference_data_events(exchange="kalshi").to_dataframe()

# Events by venue-native ticker or slug, matched across venues
fed = client.reference_data_events(event_tickers=["KXFEDDECISION-27DEC"]).to_dataframe()
Shell
curl --compressed "https://api.coinmetrics.io/v4/reference-data/events?events=kalshi-KXFEDDECISION-27DEC,polymarket-largest-company-end-of-december-2026&page_size=10000&api_key=$CM_API_KEY"
Python
import os
import requests

response = requests.get(
"https://api.coinmetrics.io/v4/reference-data/events",
params={
"events": "kalshi-KXFEDDECISION-27DEC,polymarket-largest-company-end-of-december-2026",
"page_size": 10000,
"api_key": os.environ["CM_API_KEY"],
},
).json()

Results are ordered by exchange, then by event ticker. Responses are paginated: the Python client follows pagination automatically, while direct HTTP callers page through results using next_page_token. The default listing holds only live events, which keeps it to tens of thousands of events per venue, while include_expired=true returns a venue's full history of hundreds of thousands. For a full pull of a venue, format=json_stream returns every matching event in a single streamed response without paging. Full parameter reference: see the API Reference for /reference-data/events.

Examples​

Example: Kalshi event​

A live Kalshi event for a Federal Reserve rate decision. Its markets are mutually exclusive (one per possible decision), and the venue-specific subtitle, strike_date, and collateral_return_type fields are populated. Run this query.

{
"data": [
{
"event": "kalshi-KXFEDDECISION-27DEC",
"exchange": "kalshi",
"event_ticker": "KXFEDDECISION-27DEC",
"last_updated_time": "2026-09-30T10:21:55.614640000Z",
"title": "Fed decision in Dec 2027?",
"subtitle": "On Dec 8, 2027",
"category": "Economics",
"series_ticker": "KXFEDDECISION",
"series_title": "Fed meeting",
"series_frequency": "custom",
"mutually_exclusive": true,
"status": "active",
"close_time": "2027-12-08T18:59:00.000000000Z",
"strike_date": "2027-12-08T19:00:00.000000000Z",
"collateral_return_type": "MECNET"
}
]
}

Example: Polymarket event​

A live Polymarket event that belongs to a monthly series. Polymarket events carry a description with the resolution rules, a created_time, the negative-risk flags, and venue tags (truncated here to two of six). Run this query.

{
"data": [
{
"event": "polymarket-largest-company-end-of-december-2026",
"exchange": "polymarket",
"event_ticker": "largest-company-end-of-december-2026",
"last_updated_time": "2026-09-30T10:12:30.693328000Z",
"title": "Largest Company end of December 2026?",
"description": "This market will resolve to the largest company in the world by market cap on December 31, 2026, as of market close.\n\nThe resolution source for this market will be a consensus of credible reporting.",
"series_ticker": "largest-company",
"series_title": "Largest Company",
"series_frequency": "monthly",
"mutually_exclusive": true,
"status": "active",
"created_time": "2025-11-12T21:22:24.034798000Z",
"close_time": "2027-01-01T04:59:00.000000000Z",
"neg_risk_enabled": true,
"neg_risk_augmented": true,
"tags": [
{
"id": "100328",
"slug": "economy",
"label": "Economy",
"createdAt": "2024-08-05T05:34:54.235643Z",
"forceShow": false,
"updatedAt": "2026-04-17T20:29:06.948273Z"
},
{
"id": "107",
"slug": "business",
"label": "Business",
"createdAt": "2023-11-02T21:20:37.488Z",
"forceShow": false,
"updatedAt": "2026-04-17T20:26:28.786983Z",
"publishedAt": "2023-11-02 21:20:37.48+00"
}
]
}
]
}

Example: closed event looked up by identifier​

A Kalshi event that has already closed. Because it is requested by its identifier, it is returned without include_expired=true. Run this query.

{
"data": [
{
"event": "kalshi-APPLEFOLD",
"exchange": "kalshi",
"event_ticker": "APPLEFOLD",
"last_updated_time": "2026-09-30T10:21:28.247377000Z",
"title": "When will Apple announce foldable iPhone?",
"subtitle": "Announcement",
"category": "Financials",
"series_ticker": "KXAPPLEFOLD",
"series_title": "Apple reveals foldable iPhone",
"series_frequency": "custom",
"mutually_exclusive": false,
"status": "closed",
"close_time": "2026-09-09T18:32:35.000000000Z"
}
]
}

Usage​

The standard workflow is to find the events you care about here, resolve them to their markets, and then pull market data for those markets.

  1. Discover events. Query /reference-data/events by exchange to list live events, and add include_expired=true for history. Classify them with category, tags, or series_ticker, which groups recurring events such as each scheduled rate decision.
  2. Resolve events to markets. Query /reference-data/markets with type=event_contract and the same exchange, and keep the markets whose event field matches (see Joining markets to events). The Kalshi event kalshi-KXFEDDECISION-27DEC, for example, resolves to five markets such as kalshi-KXFEDDECISION-27DEC-H25-event_contract, one per possible decision. A venue's live market listing runs to hundreds of thousands of markets, and on Kalshi most of them have already settled (see Liveness and expired markets), so use format=json_stream for this pull. Add include_expired=true only when you need the markets of closed events, because it extends the pull to the venue's full history of millions of markets.
  3. Pull market data. Check the time range available for those markets with /catalog-v2/market-trades (an explicit markets list needs no type), then call /timeseries/market-trades, /timeseries/market-candles, or /timeseries/market-openinterest with the market list and type=event_contract. The timeseries endpoints reject prediction markets when type=event_contract is missing.

The event metadata also shapes how the market data is read. When mutually_exclusive is true, the event's markets are alternative answers to one question, so their prices can be read together as probabilities across those alternatives. When it is false, the markets are separate Yes or No questions whose outcomes can overlap.

Limitations​

  • Field availability varies by venue. Each venue exposes a different set of fields, and fields a venue does not provide are omitted. Polymarket rarely populates category, so use tags to classify Polymarket events.
  • Vocabularies are not harmonized. Values of status, category, and series_frequency are venue-specific, for example fifteen_min on Kalshi and 15m on Polymarket for the same recurrence.
  • Derived status and close time can lag. Kalshi status and close_time, and Polymarket close_time for open events, are derived from the event's markets and are refreshed when the event itself is collected again.
  • status and liveness can disagree. An event can still report active after its close_time has passed, either because the venue has not yet closed it (typically while it awaits resolution) or because its derived status has not been refreshed. Such events are excluded from default listings and need include_expired=true.
  • Events without a close time are treated as expired. An event whose close_time is unknown is not returned by default and needs include_expired=true or an explicit events or event_tickers filter.
  • Series metadata can be missing. A small share of Kalshi events have no series_title or series_frequency, and Polymarket events that belong to more than one series report only the first.
  • Multivariate events are excluded. Kalshi multivariate (combination) events are not collected.
  • No market filter by event. /reference-data/markets has no parameter to select the markets of one event, so resolving an event to its markets means filtering a venue's markets on the event field.

FAQ​

Why doesn't an event appear when I list a venue's events?​

By default, a listing returns only live events, meaning events whose close_time is in the future. Events that have closed, or whose close time is unknown, are returned when you add include_expired=true, or when you request them directly with events or event_tickers.

Is status comparable across Kalshi and Polymarket?​

Only at a coarse level. Both venues use active for an event that is open and closed for one that has closed. Kalshi adds inactive for an event none of whose markets is open for trading although some have not closed, and Polymarket adds archived. For a single cross-venue test of whether an event is still open, compare close_time with the current time, which is the rule the endpoint itself uses for liveness. The two signals can disagree, because an event can stay active for a while after its close_time passes.