Skip to content

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

live = client.live_data.get_typed("sports_game", "ms_abc")

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

LiveDataResource(transport: SyncTransport)

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

AsyncLiveDataResource(transport: AsyncTransport)

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.