Live data¶
Real-time state for markets — either keyed by a milestone (score, clock, period, weather, …) or by an event ticker (crypto charts, commodity timeseries, weather observations).
Public — no auth required.
Quick reference¶
| Method | Endpoint |
|---|---|
get(milestone_id, *, include_player_stats=None) |
GET /live_data/milestone/{milestone_id} |
get_event(event_ticker, *, range=None) |
GET /live_data/events/{event_ticker} |
batch(milestone_ids, *, include_player_stats=None) |
GET /live_data/batch |
game_stats(milestone_id) |
GET /live_data/milestone/{milestone_id}/game_stats |
get_typed(milestone_type, milestone_id) |
GET /live_data/{type}/milestone/{milestone_id} (legacy) |
Get one milestone's live data¶
live = client.live_data.get("ms_abc", include_player_stats=True)
print(live.type, live.milestone_id, live.details)
LiveData.details is a loose dict[str, Any] — the shape varies by
type (football vs political race vs weather).
Get event-keyed live data¶
live = client.live_data.get_event("KXBTCD-25", range="1h")
print(live.type, live.details, live.default_range, live.range_options)
print(live.is_historical) # True for matured crypto snapshots
EventLiveData has no milestone_id. Optional range is a chart-window
hint (15min, 1h, 1d, …) when the underlying type supports it.
Batch (up to 100 milestones)¶
entries = client.live_data.batch(
milestone_ids=["ms_a", "ms_b", "ms_c"],
include_player_stats=False,
)
for entry in entries:
print(entry.milestone_id, entry.type, entry.details)
milestone_ids is required and non-empty — passing [] raises ValueError.
Cap: 100 ids per call.
Game stats / play-by-play¶
resp = client.live_data.game_stats("ms_abc")
if resp.pbp is None:
print("no play-by-play for this milestone type")
else:
for period in resp.pbp.periods:
for event in period.events:
print(event) # free-form dict; shape varies by sport
game_stats works only for sports milestones with play-by-play coverage.
Other milestone types return pbp=None. Each period's events is a list of
loose dicts (no fixed play schema upstream).
Legacy get_typed¶
Prefer get() over get_typed(). The latter wraps the legacy
/live_data/{type}/milestone/{id} path and is retained only for callers that
still depend on it. The Python kwarg is milestone_type (not type) to avoid
shadowing the built-in; the wire path still uses {type}.
Reference¶
kalshi.resources.live_data.LiveDataResource ¶
Bases: SyncResource
Sync live-data API — public, no auth required per spec.
get_event ¶
get_event(
event_ticker: str,
*,
range: str | None = None,
extra_headers: dict[str, str] | None = None
) -> EventLiveData
GET /live_data/events/{event_ticker} — event-keyed live data.
Serves crypto price charts, commodity timeseries, weather observations,
and similar event-scoped payloads. Optional range is a chart-window
hint (e.g. 15min, 1h, 1d) when the underlying type supports it.
get_typed ¶
get_typed(
milestone_type: str,
milestone_id: str,
*,
include_player_stats: bool | None = None,
extra_headers: dict[str, str] | None = None
) -> LiveData
Legacy /live_data/{type}/milestone/{milestone_id} URL form.
Despite the name, get_typed is not a more-strongly-typed
variant of :meth:get: both return the same :class:LiveData
model with the same annotations. The only difference is the URL
— get_typed hits the legacy path that embeds the milestone
{type} segment, while :meth:get hits the canonical
/live_data/milestone/{milestone_id}. The _typed suffix
refers to the typed URL form, not to Python typing.
milestone_type populates the {type} path segment. Named
milestone_type (not type) to avoid shadowing the Python
built-in.
Prefer :meth:get. The spec marks this endpoint as the legacy
form retained for backward compatibility.
batch ¶
batch(
*,
milestone_ids: list[str],
include_player_stats: bool | None = None,
extra_headers: dict[str, str] | None = None
) -> builtins.list[LiveData]
Fetch up to 100 milestones in one call.
Spec requires at least one milestone id (max 100). milestone_ids
wire format is ?milestone_ids=a&milestone_ids=b (spec
style: form, explode: true) — httpx serializes list values
that way by default.
game_stats ¶
game_stats(
milestone_id: str,
*,
extra_headers: dict[str, str] | None = None
) -> GetGameStatsResponse
Play-by-play stats. Returns pbp=None for unsupported sports.
kalshi.resources.live_data.AsyncLiveDataResource ¶
Bases: AsyncResource
Async live-data API.
get_event
async
¶
get_event(
event_ticker: str,
*,
range: str | None = None,
extra_headers: dict[str, str] | None = None
) -> EventLiveData
GET /live_data/events/{event_ticker} — event-keyed live data.
Async counterpart of :meth:LiveDataResource.get_event.
get_typed
async
¶
get_typed(
milestone_type: str,
milestone_id: str,
*,
include_player_stats: bool | None = None,
extra_headers: dict[str, str] | None = None
) -> LiveData
Legacy /live_data/{type}/milestone/{milestone_id} URL form.
Despite the name, get_typed is not a more-strongly-typed
variant of :meth:get: both return the same :class:LiveData
model with the same annotations. The only difference is the URL
— get_typed hits the legacy path that embeds the milestone
{type} segment, while :meth:get hits the canonical
/live_data/milestone/{milestone_id}. The _typed suffix
refers to the typed URL form, not to Python typing.
milestone_type populates the {type} path segment. Named
milestone_type (not type) to avoid shadowing the Python
built-in.
Prefer :meth:get. The spec marks this endpoint as the legacy
form retained for backward compatibility.
batch
async
¶
batch(
*,
milestone_ids: list[str],
include_player_stats: bool | None = None,
extra_headers: dict[str, str] | None = None
) -> builtins.list[LiveData]
Fetch up to 100 milestones in one call.
Spec requires at least one milestone id (max 100). milestone_ids
wire format is ?milestone_ids=a&milestone_ids=b (spec
style: form, explode: true) — httpx serializes list values
that way by default.
game_stats
async
¶
game_stats(
milestone_id: str,
*,
extra_headers: dict[str, str] | None = None
) -> GetGameStatsResponse
Play-by-play stats. Returns pbp=None for unsupported sports.