Kalshi Market Schema: Fields, Pitfalls and Normalization

Kalshi Market Schema: Fields, Pitfalls and Normalization

Kalshi Market Schema: Fields, Pitfalls and Normalization

A developer's reference to the Kalshi market schema: series, event and market hierarchy, price fields, settlement data, and how to normalize it across venues.

Kalshi Market Schema: Fields, Pitfalls and Normalization

The Kalshi market schema is a three-level hierarchy of series, event and market, where each market is a single binary contract with its own ticker, quotes, volume, status and settlement fields. Parsing it correctly means treating tickers as opaque keys, reading prices as dollar values rather than integer cents, and mapping the result into a venue-neutral structure before it touches your models. This guide walks through each part, then shows a normalized schema you can build against.

Assymetrixdata.assymetrix.comWork with Unified Market DataAssymetrix brings Polymarket, Kalshi, and Limitless data into one API for developers and researchers building normalized prediction market pipelines.Explore the Data API

TL;DR

  • Kalshi organizes data as series, then events, then markets. The market is the tradable unit and the row you will store most often.

  • Prices are moving from integer cents to dollar strings that allow sub-cent precision. Parse the _dollars fields and stop assuming whole numbers.

  • Units differ between REST and WebSocket channels, so convert at the edge of your pipeline, not inside strategy code.

  • A normalized market schema keeps the venue’s native ID next to a canonical ID, so you can join across venues without losing the original record.

  • Settlement fields appear only after a market resolves, so model them as nullable.

Table of Contents

  • How Kalshi structures its data

  • Core fields on a market object

  • Prices: from cents to dollar strings

  • Settlement and resolution fields

  • Recent versus archived records

  • Why you need a normalized market schema

  • Common parsing mistakes

  • Perspective

  • Using the Data API instead of building the layer yourself

  • Testing your schema before it reaches production

  • FAQ

  • Sources

How Kalshi structures its data

Kalshi data nests in three levels. A series is a recurring template, such as a monthly economic release or a daily price question. An event is one occurrence of that template, tied to a specific date or outcome set. A market is one yes/no contract inside the event.


Kalshi series event and market hierarchy

Tickers encode this hierarchy. A typical market ticker reads as a series prefix, a date-like event segment and sometimes a strike or outcome suffix. The format is useful for humans, but treat it as an opaque string in code. Prefixes and conventions have changed over time, and a parser that slices a ticker by position will break the first time a series is renamed or reissued.

The practical rule: store series_ticker, event_ticker and ticker as three separate columns, never derive one from another, and index all three. The list endpoint lets you filter by series and by parent event, which is how you pull a full event with all its strikes in one call.

If you are still wiring up access, our Kalshi API tutorial on real-time event market data covers authentication, pagination and the first requests.

Core fields on a market object

A market object carries identity, description, quotes, activity and lifecycle fields. The groups below are what most pipelines need.

Group

Typical fields

Notes

Identity

ticker, event_ticker

ticker is the primary key within Kalshi.

Description

title, subtitle

Free text. Wording differs by series and can change.

Quotes

yes_bid, yes_ask, no_bid, no_ask, last_price

Legacy cent fields, with dollar-string equivalents alongside.

Activity

volume, volume_24h, open_interest

Counts of contracts, not dollars.

Lifecycle

status, close_time, expiration_time

Timestamps are ISO 8601 in UTC.

Resolution

result, settlement fields

Empty or null until the market is determined.

Two details trip people up early. First, a binary market has two sides, and the YES and NO quotes are mirror images of each other: a YES bid at a given price implies a NO ask at its complement. Do not double count them as separate liquidity. Second, title and subtitle are display text. Never use them as keys or to infer the underlying question type.

Prices: from cents to dollar strings

This is the change most likely to break existing code. Kalshi has been migrating price fields from integer cents to dollar strings that support sub-cent increments. According to a reference built from the exchange’s API changelog, sub-penny fields were added to price APIs on August 31, 2025, extended to WebSocket messages in September 2025, and followed by additional quote fields such as yes_bid_dollars in November 2025. The same reference advises parsing the dollar fields now, because the legacy integer fields are deprecated and may be removed.

What that means in practice:

  1. Use Decimal, not float, for dollar strings. A value like "0.4800" should become a decimal type at ingestion.

  2. Never assume a tick size of one cent. Markets can define a price level structure with ranges and steps, so validate prices against the market’s own structure when you simulate orders.

  3. Handle both shapes during transition. Prefer the dollar field when present, fall back to cents divided by 100, and log whenever you hit the fallback so you know when it is safe to remove.

The order book follows the same pattern, with cent-based arrays next to dollar-string arrays. The WebSocket side adds a trap of its own: units can differ by channel, so a ticker message may give cents in one field and a dollar string in another. Write one conversion function per channel and unit test it against a captured message.

Settlement and resolution fields

Resolution data is only meaningful after a market is determined. The same reference lists settlement fields added to the market response at the end of 2025: a settlement timestamp, a settlement value in cents for the YES side, a dollar-string version of that value, and a timer for the delay between determination and settlement.

Model these as nullable columns. A common bug is to treat an empty result string as a NO outcome, which silently corrupts calibration and backtest labels. Use an explicit state machine instead:

  • open: trading is live, no outcome.

  • closed: trading has stopped, outcome may not be determined yet.

  • determined: an outcome exists, settlement may still be pending.

  • settled: payout complete, settlement timestamp present.

Exact status values can differ from this simplified list, so check the current API reference and keep your own enum mapped to the exchange’s strings rather than using them directly.

Recent versus archived records

Research workflows hit a second structural issue: Kalshi separates recent and archived data, with different cutoffs for different record types such as markets, trades and fills. A query that works for last week may return nothing for last year if you hit the wrong endpoint. When you assemble a research set, record which endpoint and cutoff produced each row, and keep a coverage table by date range so gaps are visible rather than silent.

For reproducible backtests, snapshot raw responses before transforming them. Normalization logic will change, and you want to rerun it against the original payloads.

Why you need a normalized market schema

The Kalshi schema is clean on its own terms. The trouble starts when you add a second venue. Other venues identify markets with different keys, describe outcomes differently, quote prices in different units and publish timestamps in different formats. A strategy that compares prices across venues will quietly produce wrong signals if any of those differences leak through.

A normalized market schema solves this by defining one internal shape that every venue maps into. The goal is not to erase venue differences but to isolate them in one layer. Our guide to preserving IDs and fixing parsing between Kalshi and Polymarket covers the most common mismatches in detail.

A practical normalized shape

Below is a minimal design. Field names are illustrative, so adapt them to your stack.

Field

Type

Purpose

canonical_id

string

Your stable key, independent of any venue.

venue

enum

Source venue name.

venue_market_id

string

The native key, preserved exactly (Kalshi ticker).

venue_event_id

string

Parent grouping (Kalshi event_ticker).

series_id

string, nullable

Recurring template, where the venue has one.

question

string

Display text. Never a key.

outcome_type

enum

Binary, categorical or scalar.

yes_bid, yes_ask

decimal

Dollars, between 0 and 1.

volume_contracts

decimal

Contract count, not dollars.

open_time, close_time

timestamp (UTC)

Lifecycle.

status

enum

Your own lifecycle states.

resolution_value

decimal, nullable

Final value for the YES side.

resolved_at

timestamp, nullable

Settlement time.

raw

JSON

Original payload for audit and replay.

Three design choices make this hold up over time.

Keep the native ID. Never overwrite a venue key with your own. The canonical ID is an additional join key. When a venue reissues or corrects a market, the native ID is how you find the original record.

Normalize units once. Prices go to decimal dollars between 0 and 1 on the way in. Timestamps go to UTC. Volume gets a unit suffix in its name so no one mixes contracts and dollars later.

Keep the raw payload. Storing the original JSON costs little and saves a re-ingestion when the venue adds a field.

Mapping logic that does not rot

The hardest part of normalization is deciding that two markets from different venues ask the same question. Matching on title text is fragile. A more durable approach combines structured attributes: the underlying event or entity, the resolution date, the threshold or outcome, and the resolution source. Treat a match as a relationship with a confidence level and keep the mapping in its own table, so a bad match can be corrected without touching market rows. Our write-up on market metadata in prediction markets goes deeper on which attributes are worth capturing.

Resolution rules deserve special attention. Two contracts can share a title yet resolve on different sources or at different times, which makes an apparent price gap an artifact rather than an opportunity. If you measure gaps, our quant measurement guide to cross-venue price gaps shows how to filter for that.

Common parsing mistakes

  • Slicing tickers. Positional parsing breaks when conventions change. Store the segments the API gives you.

  • Float arithmetic on prices. Rounding errors accumulate in PnL and calibration tests.

  • Using the midpoint as truth. A wide spread makes the midpoint meaningless. Store bid, ask and last separately.

  • Ignoring pagination cursors. Large listings are paged, so a loop that stops after one page undercounts.

  • Treating missing as zero. A null settlement value is not a zero payout.

  • Mixing REST and WebSocket units. Convert each channel with its own tested function.

Perspective

We built Assymetrix around one observation: the schema is the product. Every venue publishes data that is internally consistent, and every team that consumes two venues ends up writing the same translation layer, usually under deadline, usually with a few silent bugs.

Our position is that this layer should be venue-neutral and maintained once. That is why we aggregate Polymarket, Kalshi and Limitless into a single normalized schema with canonical IDs, built on roughly 1.5 terabytes of history across nearly one billion rows of trading activity.

Neutrality matters here. A schema owned by one exchange will reflect that exchange’s conventions. A neutral one can keep each venue’s native identifiers intact while giving researchers and bots a consistent shape to query. In our view, that is the foundation for reproducible backtests, honest cross-venue signals and agents that can reason over several venues without custom glue per source.

Dean

Using the Data API instead of building the layer yourself

If you would rather spend your time on strategy than on field mapping, the Assymetrix Data API gives developers, quants, bots and AI agents unified access to Kalshi, Polymarket and Limitless through one integration, with normalized market schemas and real-time and historical coverage.


Assymetrix

For a closer look at how the feeds are organized, see the prediction market data feed guide. Developers working in JavaScript can follow the Node.js walkthrough to fetch, stream and normalize, and bot builders will find the Kalshi trading bot developer guide useful for the execution side.

Testing your schema before it reaches production

A schema is only as reliable as the tests around it. Four checks catch most problems:

  1. Round-trip test. Ingest a raw payload, normalize it, and confirm the native ID and raw JSON survive unchanged.

  2. Bounds test. Every normalized price must fall between 0 and 1, and a YES bid must not exceed the YES ask.

  3. Unit test per channel. Feed one captured REST message and one WebSocket message for the same market and assert identical normalized output.

  4. Replay test. Rerun a past day through the pipeline and compare the result with the stored output. Differences point to mapping changes, not market changes.

If you want to exercise a strategy against historical rows before risking capital, our guide to paper trading prediction markets with replayed API data shows the workflow.

FAQ

What are the main objects in the Kalshi market schema?

Kalshi uses three levels: series, event and market. A series is a recurring template, an event is one occurrence of it, and a market is a single yes/no contract inside the event. Store all three identifiers as separate fields.

Should I still use the cent-based price fields?

Prefer the dollar-string fields. Kalshi has been adding sub-cent precision, and a reference built from its changelog says the legacy integer fields are deprecated and may be removed. Parse dollar strings into a decimal type, and fall back to cents only while you transition.

What is a normalized market schema?

It is one internal data shape that every venue’s markets are mapped into, with consistent units, timestamps, statuses and IDs. It usually keeps the venue’s native ID alongside a canonical ID, so you can join across venues and still trace each row to its source.

How do I match a Kalshi market to the same question on another venue?

Compare structured attributes rather than titles: the underlying event, resolution date, threshold and resolution source. Store the match as a separate mapping with a confidence level, so you can correct it without changing the market rows.

Why do my REST and WebSocket prices disagree?

Units can differ between channels, with some fields in cents and others in dollar strings. Write a dedicated conversion for each channel and test it against captured messages.

Sources

Kalshi Market Schema: Fields, Pitfalls and Normalization

The Kalshi market schema is a three-level hierarchy of series, event and market, where each market is a single binary contract with its own ticker, quotes, volume, status and settlement fields. Parsing it correctly means treating tickers as opaque keys, reading prices as dollar values rather than integer cents, and mapping the result into a venue-neutral structure before it touches your models. This guide walks through each part, then shows a normalized schema you can build against.

Assymetrixdata.assymetrix.comWork with Unified Market DataAssymetrix brings Polymarket, Kalshi, and Limitless data into one API for developers and researchers building normalized prediction market pipelines.Explore the Data API

TL;DR

  • Kalshi organizes data as series, then events, then markets. The market is the tradable unit and the row you will store most often.

  • Prices are moving from integer cents to dollar strings that allow sub-cent precision. Parse the _dollars fields and stop assuming whole numbers.

  • Units differ between REST and WebSocket channels, so convert at the edge of your pipeline, not inside strategy code.

  • A normalized market schema keeps the venue’s native ID next to a canonical ID, so you can join across venues without losing the original record.

  • Settlement fields appear only after a market resolves, so model them as nullable.

Table of Contents

  • How Kalshi structures its data

  • Core fields on a market object

  • Prices: from cents to dollar strings

  • Settlement and resolution fields

  • Recent versus archived records

  • Why you need a normalized market schema

  • Common parsing mistakes

  • Perspective

  • Using the Data API instead of building the layer yourself

  • Testing your schema before it reaches production

  • FAQ

  • Sources

How Kalshi structures its data

Kalshi data nests in three levels. A series is a recurring template, such as a monthly economic release or a daily price question. An event is one occurrence of that template, tied to a specific date or outcome set. A market is one yes/no contract inside the event.


Kalshi series event and market hierarchy

Tickers encode this hierarchy. A typical market ticker reads as a series prefix, a date-like event segment and sometimes a strike or outcome suffix. The format is useful for humans, but treat it as an opaque string in code. Prefixes and conventions have changed over time, and a parser that slices a ticker by position will break the first time a series is renamed or reissued.

The practical rule: store series_ticker, event_ticker and ticker as three separate columns, never derive one from another, and index all three. The list endpoint lets you filter by series and by parent event, which is how you pull a full event with all its strikes in one call.

If you are still wiring up access, our Kalshi API tutorial on real-time event market data covers authentication, pagination and the first requests.

Core fields on a market object

A market object carries identity, description, quotes, activity and lifecycle fields. The groups below are what most pipelines need.

Group

Typical fields

Notes

Identity

ticker, event_ticker

ticker is the primary key within Kalshi.

Description

title, subtitle

Free text. Wording differs by series and can change.

Quotes

yes_bid, yes_ask, no_bid, no_ask, last_price

Legacy cent fields, with dollar-string equivalents alongside.

Activity

volume, volume_24h, open_interest

Counts of contracts, not dollars.

Lifecycle

status, close_time, expiration_time

Timestamps are ISO 8601 in UTC.

Resolution

result, settlement fields

Empty or null until the market is determined.

Two details trip people up early. First, a binary market has two sides, and the YES and NO quotes are mirror images of each other: a YES bid at a given price implies a NO ask at its complement. Do not double count them as separate liquidity. Second, title and subtitle are display text. Never use them as keys or to infer the underlying question type.

Prices: from cents to dollar strings

This is the change most likely to break existing code. Kalshi has been migrating price fields from integer cents to dollar strings that support sub-cent increments. According to a reference built from the exchange’s API changelog, sub-penny fields were added to price APIs on August 31, 2025, extended to WebSocket messages in September 2025, and followed by additional quote fields such as yes_bid_dollars in November 2025. The same reference advises parsing the dollar fields now, because the legacy integer fields are deprecated and may be removed.

What that means in practice:

  1. Use Decimal, not float, for dollar strings. A value like "0.4800" should become a decimal type at ingestion.

  2. Never assume a tick size of one cent. Markets can define a price level structure with ranges and steps, so validate prices against the market’s own structure when you simulate orders.

  3. Handle both shapes during transition. Prefer the dollar field when present, fall back to cents divided by 100, and log whenever you hit the fallback so you know when it is safe to remove.

The order book follows the same pattern, with cent-based arrays next to dollar-string arrays. The WebSocket side adds a trap of its own: units can differ by channel, so a ticker message may give cents in one field and a dollar string in another. Write one conversion function per channel and unit test it against a captured message.

Settlement and resolution fields

Resolution data is only meaningful after a market is determined. The same reference lists settlement fields added to the market response at the end of 2025: a settlement timestamp, a settlement value in cents for the YES side, a dollar-string version of that value, and a timer for the delay between determination and settlement.

Model these as nullable columns. A common bug is to treat an empty result string as a NO outcome, which silently corrupts calibration and backtest labels. Use an explicit state machine instead:

  • open: trading is live, no outcome.

  • closed: trading has stopped, outcome may not be determined yet.

  • determined: an outcome exists, settlement may still be pending.

  • settled: payout complete, settlement timestamp present.

Exact status values can differ from this simplified list, so check the current API reference and keep your own enum mapped to the exchange’s strings rather than using them directly.

Recent versus archived records

Research workflows hit a second structural issue: Kalshi separates recent and archived data, with different cutoffs for different record types such as markets, trades and fills. A query that works for last week may return nothing for last year if you hit the wrong endpoint. When you assemble a research set, record which endpoint and cutoff produced each row, and keep a coverage table by date range so gaps are visible rather than silent.

For reproducible backtests, snapshot raw responses before transforming them. Normalization logic will change, and you want to rerun it against the original payloads.

Why you need a normalized market schema

The Kalshi schema is clean on its own terms. The trouble starts when you add a second venue. Other venues identify markets with different keys, describe outcomes differently, quote prices in different units and publish timestamps in different formats. A strategy that compares prices across venues will quietly produce wrong signals if any of those differences leak through.

A normalized market schema solves this by defining one internal shape that every venue maps into. The goal is not to erase venue differences but to isolate them in one layer. Our guide to preserving IDs and fixing parsing between Kalshi and Polymarket covers the most common mismatches in detail.

A practical normalized shape

Below is a minimal design. Field names are illustrative, so adapt them to your stack.

Field

Type

Purpose

canonical_id

string

Your stable key, independent of any venue.

venue

enum

Source venue name.

venue_market_id

string

The native key, preserved exactly (Kalshi ticker).

venue_event_id

string

Parent grouping (Kalshi event_ticker).

series_id

string, nullable

Recurring template, where the venue has one.

question

string

Display text. Never a key.

outcome_type

enum

Binary, categorical or scalar.

yes_bid, yes_ask

decimal

Dollars, between 0 and 1.

volume_contracts

decimal

Contract count, not dollars.

open_time, close_time

timestamp (UTC)

Lifecycle.

status

enum

Your own lifecycle states.

resolution_value

decimal, nullable

Final value for the YES side.

resolved_at

timestamp, nullable

Settlement time.

raw

JSON

Original payload for audit and replay.

Three design choices make this hold up over time.

Keep the native ID. Never overwrite a venue key with your own. The canonical ID is an additional join key. When a venue reissues or corrects a market, the native ID is how you find the original record.

Normalize units once. Prices go to decimal dollars between 0 and 1 on the way in. Timestamps go to UTC. Volume gets a unit suffix in its name so no one mixes contracts and dollars later.

Keep the raw payload. Storing the original JSON costs little and saves a re-ingestion when the venue adds a field.

Mapping logic that does not rot

The hardest part of normalization is deciding that two markets from different venues ask the same question. Matching on title text is fragile. A more durable approach combines structured attributes: the underlying event or entity, the resolution date, the threshold or outcome, and the resolution source. Treat a match as a relationship with a confidence level and keep the mapping in its own table, so a bad match can be corrected without touching market rows. Our write-up on market metadata in prediction markets goes deeper on which attributes are worth capturing.

Resolution rules deserve special attention. Two contracts can share a title yet resolve on different sources or at different times, which makes an apparent price gap an artifact rather than an opportunity. If you measure gaps, our quant measurement guide to cross-venue price gaps shows how to filter for that.

Common parsing mistakes

  • Slicing tickers. Positional parsing breaks when conventions change. Store the segments the API gives you.

  • Float arithmetic on prices. Rounding errors accumulate in PnL and calibration tests.

  • Using the midpoint as truth. A wide spread makes the midpoint meaningless. Store bid, ask and last separately.

  • Ignoring pagination cursors. Large listings are paged, so a loop that stops after one page undercounts.

  • Treating missing as zero. A null settlement value is not a zero payout.

  • Mixing REST and WebSocket units. Convert each channel with its own tested function.

Perspective

We built Assymetrix around one observation: the schema is the product. Every venue publishes data that is internally consistent, and every team that consumes two venues ends up writing the same translation layer, usually under deadline, usually with a few silent bugs.

Our position is that this layer should be venue-neutral and maintained once. That is why we aggregate Polymarket, Kalshi and Limitless into a single normalized schema with canonical IDs, built on roughly 1.5 terabytes of history across nearly one billion rows of trading activity.

Neutrality matters here. A schema owned by one exchange will reflect that exchange’s conventions. A neutral one can keep each venue’s native identifiers intact while giving researchers and bots a consistent shape to query. In our view, that is the foundation for reproducible backtests, honest cross-venue signals and agents that can reason over several venues without custom glue per source.

Dean

Using the Data API instead of building the layer yourself

If you would rather spend your time on strategy than on field mapping, the Assymetrix Data API gives developers, quants, bots and AI agents unified access to Kalshi, Polymarket and Limitless through one integration, with normalized market schemas and real-time and historical coverage.


Assymetrix

For a closer look at how the feeds are organized, see the prediction market data feed guide. Developers working in JavaScript can follow the Node.js walkthrough to fetch, stream and normalize, and bot builders will find the Kalshi trading bot developer guide useful for the execution side.

Testing your schema before it reaches production

A schema is only as reliable as the tests around it. Four checks catch most problems:

  1. Round-trip test. Ingest a raw payload, normalize it, and confirm the native ID and raw JSON survive unchanged.

  2. Bounds test. Every normalized price must fall between 0 and 1, and a YES bid must not exceed the YES ask.

  3. Unit test per channel. Feed one captured REST message and one WebSocket message for the same market and assert identical normalized output.

  4. Replay test. Rerun a past day through the pipeline and compare the result with the stored output. Differences point to mapping changes, not market changes.

If you want to exercise a strategy against historical rows before risking capital, our guide to paper trading prediction markets with replayed API data shows the workflow.

FAQ

What are the main objects in the Kalshi market schema?

Kalshi uses three levels: series, event and market. A series is a recurring template, an event is one occurrence of it, and a market is a single yes/no contract inside the event. Store all three identifiers as separate fields.

Should I still use the cent-based price fields?

Prefer the dollar-string fields. Kalshi has been adding sub-cent precision, and a reference built from its changelog says the legacy integer fields are deprecated and may be removed. Parse dollar strings into a decimal type, and fall back to cents only while you transition.

What is a normalized market schema?

It is one internal data shape that every venue’s markets are mapped into, with consistent units, timestamps, statuses and IDs. It usually keeps the venue’s native ID alongside a canonical ID, so you can join across venues and still trace each row to its source.

How do I match a Kalshi market to the same question on another venue?

Compare structured attributes rather than titles: the underlying event, resolution date, threshold and resolution source. Store the match as a separate mapping with a confidence level, so you can correct it without changing the market rows.

Why do my REST and WebSocket prices disagree?

Units can differ between channels, with some fields in cents and others in dollar strings. Write a dedicated conversion for each channel and test it against captured messages.

Sources

Other Blog