API Reference¶
Auto-generated from docstrings on the public surface of the kalshi package.
For narrative coverage of any of these, follow the section links — every class here also has a dedicated user-facing page elsewhere in the docs.
Clients¶
kalshi.client.KalshiClient ¶
KalshiClient(
*,
key_id: str | None = None,
private_key_path: str | Path | None = None,
private_key: str | bytes | None = None,
auth: KalshiAuth | None = None,
config: KalshiConfig | None = None,
demo: bool = False,
base_url: str | None = None,
timeout: float | None = None,
max_retries: int | None = None,
transport: BaseTransport | None = None
)
Synchronous client for the Kalshi prediction markets API.
Usage
with KalshiClient(key_id="...", private_key_path="~/.kalshi/key.pem") as client: markets = client.markets.list(status="open") for market in markets: print(market.ticker, market.yes_ask)
Or without context manager
client = KalshiClient(key_id="...", private_key_path="~/.kalshi/key.pem") try: markets = client.markets.list() finally: client.close()
is_authenticated
property
¶
Whether this client has auth credentials configured.
from_env
classmethod
¶
Create client from environment variables.
Reads
KALSHI_KEY_ID (optional — omit for unauthenticated access) KALSHI_PRIVATE_KEY (PEM string) or KALSHI_PRIVATE_KEY_PATH (file path) KALSHI_API_BASE_URL (optional, overrides base_url) KALSHI_DEMO (optional, "true" for demo environment)
Returns an unauthenticated client if no credentials are configured.
kalshi.async_client.AsyncKalshiClient ¶
AsyncKalshiClient(
*,
key_id: str | None = None,
private_key_path: str | Path | None = None,
private_key: str | bytes | None = None,
auth: KalshiAuth | None = None,
config: KalshiConfig | None = None,
demo: bool = False,
base_url: str | None = None,
timeout: float | None = None,
max_retries: int | None = None,
transport: AsyncBaseTransport | None = None
)
Asynchronous client for the Kalshi prediction markets API.
Usage
async with AsyncKalshiClient(key_id="...", private_key_path="~/.kalshi/key.pem") as client: markets = await client.markets.list(status="open") for market in markets: print(market.ticker, market.yes_ask)
is_authenticated
property
¶
Whether this client has auth credentials configured.
ws
property
¶
WebSocket client for real-time streaming.
.. note::
Each access returns a new KalshiWebSocket instance.
Per-instance state (callbacks registered via .on(),
pending subscriptions) does not carry across accesses —
assign the property to a local variable if you need to share
state across multiple operations::
ws = client.ws # capture once
@ws.on("ticker_v2")
async def handle(msg): ...
async with ws.connect() as session: ...
The sync ``KalshiClient`` does not expose ``.ws``; WebSocket
access is async-only.
Usage::
async with client.ws.connect() as session:
async for msg in session.subscribe_ticker(tickers=["ECON-GDP-25Q1"]):
print(msg.msg.yes_bid)
from_env
classmethod
¶
Create async client from environment variables.
Reads
KALSHI_KEY_ID (optional — omit for unauthenticated access) KALSHI_PRIVATE_KEY (PEM string) or KALSHI_PRIVATE_KEY_PATH (file path) KALSHI_API_BASE_URL (optional, overrides base_url) KALSHI_DEMO (optional, "true" for demo environment)
Returns an unauthenticated client if no credentials are configured.
Configuration¶
kalshi.config.KalshiConfig
dataclass
¶
KalshiConfig(
base_url: str = PRODUCTION_BASE_URL,
timeout: float = DEFAULT_TIMEOUT,
max_retries: int = DEFAULT_MAX_RETRIES,
retry_base_delay: float = 0.5,
retry_max_delay: float = 30.0,
extra_headers: dict[str, str] = dict(),
ws_base_url: str = PRODUCTION_WS_URL,
ws_max_retries: int = DEFAULT_WS_MAX_RETRIES,
http2: bool = False,
limits: Limits | None = None,
)
Client configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
base_url |
str
|
API base URL. Defaults to production. |
timeout |
float
|
Request timeout in seconds. Defaults to 30. |
max_retries |
int
|
Max retry attempts for transient errors. Defaults to 3. |
retry_base_delay |
float
|
Base delay in seconds for exponential backoff. Defaults to 0.5. |
retry_max_delay |
float
|
Maximum delay in seconds for backoff. Defaults to 30. |
http2 |
bool
|
Enable HTTP/2 for REST requests. Off by default for compat.
Requires the |
limits |
Limits | None
|
Custom |
Authentication¶
kalshi.auth.KalshiAuth ¶
RSA-PSS request signer for the Kalshi API.
The private key is loaded once at construction time and cached in memory. Thread-safe: RSA signing is stateless.
from_key_path
classmethod
¶
Load auth from a PEM private key file.
Supports ~ expansion (e.g., ~/kalshi.pem).
from_pem
classmethod
¶
Load auth from PEM-encoded private key content.
from_env
classmethod
¶
Load auth from environment variables.
Reads
KALSHI_KEY_ID (required) KALSHI_PRIVATE_KEY (PEM string) or KALSHI_PRIVATE_KEY_PATH (file path)
try_from_env
classmethod
¶
Load auth from environment variables, returning None if not configured.
Returns None if KALSHI_KEY_ID is not set, or if neither KALSHI_PRIVATE_KEY nor KALSHI_PRIVATE_KEY_PATH is set.
Note: Does not raise on missing variables, but may still raise KalshiAuthError if variables are set with invalid data (e.g., malformed PEM content or nonexistent key file path).
sign_request ¶
Sign a request and return the auth headers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
HTTP method (GET, POST, DELETE, etc.) |
required |
path
|
str
|
Full API path (e.g., /trade-api/v2/markets). Query params are stripped. |
required |
timestamp_ms
|
int | None
|
Unix timestamp in milliseconds. Auto-generated if None. |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
Dict with KALSHI-ACCESS-KEY, KALSHI-ACCESS-SIGNATURE, KALSHI-ACCESS-TIMESTAMP. |
sign_request_async
async
¶
Async variant of :meth:sign_request that offloads to a dedicated executor.
Uses loop.run_in_executor(self._sign_executor, ...) so the
~1-10 ms of RSA-PSS CPU does not block the asyncio event loop.
The executor is dedicated (not asyncio's default pool) so signs
don't queue behind getaddrinfo / file I/O / other to_thread()
work — relevant during WS reconnect storms where DNS dominates.
The sync :meth:sign_request API is unchanged; callers running
on the sync transport should keep using it.
close ¶
Shut down the sign-offload executor if one was created.
Idempotent. Safe to call without an executor having been initialised. Long-lived clients ordinarily rely on interpreter-shutdown cleanup (atexit-joined executor threads); this hook is for tests and other callers that want deterministic teardown.
Errors¶
See Errors — that page is the canonical autodoc reference for every exception class.
Pagination¶
kalshi.models.common.Page ¶
Bases: BaseModel, Generic[T]
A page of results from a list endpoint.
Iterable over items. Also exposes cursor metadata for manual pagination.
Usage
page = client.markets.list(limit=50) for item in page: # iterate items process(item) print(page.cursor) # access cursor print(page.has_next) # check if more pages exist
Custom types¶
kalshi.types ¶
Custom types and Pydantic field helpers for the Kalshi SDK.
DollarDecimal
module-attribute
¶
DollarDecimal = Annotated[
Decimal,
BeforeValidator(_coerce_decimal),
PlainSerializer(_decimal_to_str, return_type=str),
]
A Decimal field that handles bidirectional conversion for Kalshi dollar values.
- Parse: Accepts str/int/float/Decimal, converts via Decimal(str(value))
- Serialize: Outputs string representation for API requests
FixedPointCount
module-attribute
¶
FixedPointCount = Annotated[
Decimal,
BeforeValidator(_coerce_decimal),
PlainSerializer(_decimal_to_str, return_type=str),
]
A Decimal field that handles bidirectional conversion for Kalshi count/volume values.
- Parse: Accepts str/int/float/Decimal, converts via Decimal(str(value))
- Serialize: Outputs string representation for API requests
NullableList
module-attribute
¶
A list[T] that tolerates server-returned null by coercing to [].
Use on response-model fields (extra='allow') whose spec says 'array' but where the live API has been observed to return null. Do NOT use on request bodies — those should enforce strict validation.
to_decimal ¶
Convert a user-supplied price/count value to Decimal safely.
Always goes through str() to avoid float representation errors. e.g., to_decimal(0.65) returns Decimal("0.65"), not Decimal(0.65).
Request models¶
kalshi.models.orders.CreateOrderRequest ¶
Bases: BaseModel
Parameters for creating an order.
Price fields serialize with _dollars suffix. count is a Decimal
and serializes as count_fp (FixedPointCount string); the spec
accepts either count or count_fp key, but the SDK commits to
a single wire shape.
buy_max_cost is integer cents (per OpenAPI spec: "Maximum
cost in cents"). Pass e.g. 500 for a $5.00 cap, NOT 5.00.
Passing a decimal string like "5.00" raises ValidationError.
The SDK previously exposed a type: str = "limit" field never
defined in the spec's CreateOrderRequest schema. v0.8.0 removes
it. Callers passing type="market" (or similar) now get a
ValidationError at construction time.
ticker, side, and action are all required by the spec.
Pre-v2.3.0 the SDK defaulted action to "buy" as a convenience;
that default has been removed to match the spec required-set (#172).
See kalshi.resources.orders.OrdersResource.create for the
user-facing method that builds this model internally.
kalshi.models.orders.AmendOrderRequest ¶
Bases: BaseModel
Parameters for amending an open order.
Matches spec components.schemas.AmendOrderRequest. Required fields
(ticker, side, action) mirror the spec's required list.
Price fields serialize with _dollars suffix; count serializes
as count_fp (FixedPointCount).
Cent-form yes_price/no_price spec properties are NOT on this
model — redundant with the _dollars forms. EXCLUSIONS in
tests/_contract_support.py records this.
See kalshi.resources.orders.OrdersResource.amend — v0.8.0 builds
this model internally; the public method signature is unchanged.
kalshi.models.orders.DecreaseOrderRequest ¶
Bases: BaseModel
Parameters for decreasing an open order's size.
Matches spec components.schemas.DecreaseOrderRequest. Spec marks
all fields optional, but the server rejects an empty body — so the
model enforces XOR at construction: exactly one of reduce_by or
reduce_to must be set. This matches the method-level guard in
orders.decrease() and keeps model-first construction (v0.9)
fail-fast rather than deferring the error to the HTTP call.
See kalshi.resources.orders.OrdersResource.decrease — v0.8.0
builds this model internally; method signature unchanged.
kalshi.models.orders.BatchCreateOrdersRequest ¶
Bases: BaseModel
Wrapper for the POST /portfolio/orders/batched request body.
Matches spec components.schemas.BatchCreateOrdersRequest: a single
orders key holding a list of CreateOrderRequest entries. Each
nested entry inherits extra="forbid" from CreateOrderRequest
itself, so phantom fields in items fail at construction time.
See kalshi.resources.orders.OrdersResource.batch_create — v0.8.0
wraps this model internally; method signature unchanged.
kalshi.models.orders.BatchCancelOrdersRequest ¶
Bases: BaseModel
Wrapper for the DELETE /portfolio/orders/batched request body.
Matches spec components.schemas.BatchCancelOrdersRequest. Spec
defines two fields: the preferred orders (list of
BatchCancelOrdersRequestOrder) and the deprecated ids (list
of string order IDs). SDK v0.8.0 commits to emitting orders only.
The previous SDK sent the deprecated ids field — BREAKING change
at the wire level as of v0.8.0. Users calling the public
batch_cancel(orders=[...]) method are unaffected.
See kalshi.resources.orders.OrdersResource.batch_cancel.
kalshi.models.orders.BatchCancelOrdersRequestOrder ¶
Bases: BaseModel
A single cancellation entry in a batch cancel request.
Matches spec components.schemas.BatchCancelOrdersRequestOrder.
Required: order_id. Optional: subaccount (defaults to 0,
primary subaccount).
kalshi.models.api_keys.CreateApiKeyRequest ¶
Bases: BaseModel
Body for POST /api_keys — register a caller-supplied public key.
public_key must be a PEM-encoded RSA public key (i.e. starts
with -----BEGIN PUBLIC KEY-----). scopes defaults to full
access (["read", "write"]) server-side when omitted. If
"write" is included, "read" must be too.
kalshi.models.api_keys.GenerateApiKeyRequest ¶
Bases: BaseModel
Body for POST /api_keys/generate — let Kalshi mint a key pair.
kalshi.models.communications.CreateRFQRequest ¶
Bases: BaseModel
Body for POST /communications/rfqs.
Spec allows contracts (integer) or contracts_fp (fixed-point string);
SDK commits to the integer form. Target cost uses _dollars wire suffix.
target_cost_centi_cents is deprecated upstream — omitted here.
Spec only requires market_ticker and rest_remainder; contracts
and target_cost are both optional and may both be omitted (a "shopping
around" RFQ with no committed size or cost). The SDK does not enforce a
contracts-or-cost invariant because the spec doesn't.
kalshi.models.communications.CreateQuoteRequest ¶
Bases: BaseModel
Body for POST /communications/quotes.
Unlike order/amend requests, the spec names the wire fields yes_bid /
no_bid (no _dollars suffix) for this request. The Quote response
fields, however, use the _dollars suffix — handled on the response model.
kalshi.models.communications.AcceptQuoteRequest ¶
Bases: BaseModel
Body for PUT /communications/quotes/{quote_id}/accept.
kalshi.models.multivariate.CreateMarketInMultivariateEventCollectionRequest ¶
Bases: BaseModel
Parameters for POST /multivariate_event_collections/{collection_ticker}.
Matches spec components.schemas.CreateMarketInMultivariateEventCollectionRequest.
Required: selected_markets. Optional: with_market_payload.
Carve-out: extra="forbid" on this model rejects unknown top-level
keys but NOT unknown keys inside each TickerPair in
selected_markets — TickerPair itself is extra="allow" (see
its docstring for why). Phantom keys nested inside a TickerPair
currently pass through to the wire. Tracked as a v0.9 follow-up.
See kalshi.resources.multivariate.MultivariateCollectionsResource.create_market
— v0.8.0 builds this model internally; method signature unchanged.
kalshi.models.multivariate.LookupTickersForMarketInMultivariateEventCollectionRequest ¶
Bases: BaseModel
Parameters for PUT /multivariate_event_collections/{collection_ticker}/lookup.
Matches spec
components.schemas.LookupTickersForMarketInMultivariateEventCollectionRequest.
Only selected_markets, required.
Carve-out: extra="forbid" on this model rejects unknown top-level
keys but NOT unknown keys inside each TickerPair in
selected_markets — TickerPair itself is extra="allow" (see
its docstring for why). Phantom keys nested inside a TickerPair
currently pass through to the wire. Tracked as a v0.9 follow-up.
See kalshi.resources.multivariate.MultivariateCollectionsResource.lookup_tickers
— v0.8.0 builds this model internally; method signature unchanged.
kalshi.models.order_groups.CreateOrderGroupRequest ¶
Bases: BaseModel
Create body. SDK sends integer form; spec's contracts_limit_fp string variant unused.
kalshi.models.order_groups.UpdateOrderGroupLimitRequest ¶
Bases: BaseModel
Update-limit body. No subaccount — spec omits SubaccountQuery on /limit.
kalshi.models.subaccounts.ApplySubaccountTransferRequest ¶
Bases: BaseModel
Body for POST /portfolio/subaccounts/transfer.
amount_cents is integer cents per spec (matches the buy_max_cost
convention on CreateOrderRequest). Pass 500 for $5.00, never
a Decimal. from_subaccount / to_subaccount use 0 for the
primary account and a positive integer for numbered subaccounts. The
server is the source of truth for the upper bound: spec describes
1-32 in prose but defines no JSON-schema maximum, and demo has
been observed allocating values above 32. The SDK validates only the
lower bound (ge=0) so server-assigned numbers always round-trip.
kalshi.models.subaccounts.UpdateSubaccountNettingRequest ¶
Bases: BaseModel
Body for PUT /portfolio/subaccounts/netting.
Response models¶
Markets, events, series¶
kalshi.models.markets ¶
Market-related models.
MarketStatusLiteral
module-attribute
¶
Market status filter for GET /markets. Spec: MarketStatusQuery enum.
MveFilterLiteral
module-attribute
¶
Multivariate-event filter for GET /markets. Spec: MveFilterQuery enum.
Market ¶
Bases: BaseModel
A Kalshi prediction market.
Price fields accept both _dollars-suffixed names from the API
(e.g. yes_bid_dollars) and short names (e.g. yes_bid).
Volume/count fields accept both _fp-suffixed names and short names.
OrderbookLevel ¶
Bases: BaseModel
A single price level in the orderbook.
Quantity is FixedPointCount — fractional contract counts are sent as
FixedPointCount strings like "100.50" from the API.
Orderbook ¶
Bases: BaseModel
Orderbook for a market.
BidAskDistribution ¶
Bases: BaseModel
OHLC data for bid/ask prices within a candlestick period.
PriceDistribution ¶
Bases: BaseModel
OHLC data for trade prices within a candlestick period.
Fields are nullable because there may be no trades in a period.
Candlestick ¶
Bases: BaseModel
A candlestick data point for a market.
The API returns nested OHLC objects for yes_bid, yes_ask, and price, plus volume and open interest as FixedPointCount strings.
MarketCandlesticks ¶
Bases: BaseModel
Per-market candlestick bundle in a bulk response.
Spec schema MarketCandlesticksResponse — wraps a ticker and its
candlesticks. The outer bulk response is {markets: [...]}.
kalshi.models.events ¶
Event-related models.
EventStatusLiteral
module-attribute
¶
Event status filter for GET /events. Spec: GetEvents.status query enum.
Event ¶
Bases: BaseModel
A Kalshi event (container for one or more markets).
MarketMetadata ¶
Bases: BaseModel
Metadata for a market within an event.
SettlementSource ¶
Bases: BaseModel
A settlement source for an event.
EventMetadata ¶
Bases: BaseModel
Metadata for an event including images and settlement sources.
kalshi.models.series ¶
Series-related models.
Series ¶
Bases: BaseModel
A Kalshi series (template for recurring events).
SeriesFeeChange ¶
Bases: BaseModel
A scheduled fee change for a series.
EventCandlesticks ¶
Bases: BaseModel
Event-level candlestick data spanning multiple markets.
Unlike market candlesticks (flat list for one market), event candlesticks return a nested structure: one candlestick list per market in the event.
PercentilePoint ¶
Bases: BaseModel
A single forecast value at a given percentile.
ForecastPercentilesPoint ¶
Bases: BaseModel
A forecast data point with percentile breakdowns.
Trading¶
kalshi.models.orders.Order ¶
Bases: BaseModel
A Kalshi order.
Price/cost fields accept both _dollars-suffixed names from the API
(e.g. yes_price_dollars) and short names (e.g. yes_price).
kalshi.models.orders.Fill ¶
Bases: BaseModel
A filled trade.
Price fields accept both _dollars-suffixed names from the API
and short names. Count accepts _fp-suffixed name.
kalshi.models.orders.AmendOrderResponse ¶
Bases: BaseModel
Response from amending an order — contains both pre and post-amendment orders.
kalshi.models.orders.OrderQueuePosition ¶
Bases: BaseModel
Queue position for a single resting order.
kalshi.models.portfolio ¶
Portfolio-related models.
SettlementStatusLiteral
module-attribute
¶
Position settlement status filter for GET /fcm/positions. Spec: settlement_status query enum.
PaymentStatusLiteral
module-attribute
¶
Status of a Deposit/Withdrawal. Spec defines two structurally-identical inline enums (Deposit.status, Withdrawal.status); the SDK shares one alias since the values are identical.
PaymentTypeLiteral
module-attribute
¶
Payment method used for a deposit/withdrawal.
IndexedBalance ¶
Bases: BaseModel
Balance for a single exchange shard. Added by spec v3.18.0 alongside
the balance_breakdown field on :class:Balance.
Currently only exchange_index=0 is supported per spec.
Type note: balance here is DollarDecimal (fixed-point
dollar string per spec), unlike :attr:Balance.balance which is
integer cents. Same field name, different semantics — be deliberate
when reading from balance.balance_breakdown[i].balance versus
balance.balance. The :attr:Balance.balance_dollars field
rendered in dollars matches the breakdown's units.
Balance ¶
Bases: BaseModel
Account balance.
balance is integer cents (legacy field). balance_dollars is the
same value as a fixed-point dollar string, added by spec v3.18.0 and
now required on every response. balance_breakdown (optional) splits
the total across exchange shards when present.
TotalRestingOrderValue ¶
Bases: BaseModel
Total value of resting orders in cents.
Spec: "intended for use by FCM members (rare)". Non-FCM accounts see 403 on this endpoint (demo audit 2026-04-18).
MarketPosition ¶
Bases: BaseModel
A position in a single market.
EventPosition ¶
Bases: BaseModel
A position aggregated at the event level.
PositionsResponse ¶
Bases: BaseModel
Response from the positions endpoint containing both market and event positions.
Deposit ¶
Bases: BaseModel
A single deposit history entry. Amounts are integer cents.
Withdrawal ¶
Bases: BaseModel
A single withdrawal history entry. Amounts are integer cents.
Settlement ¶
Bases: BaseModel
A settled market position.
Account, subaccounts, API keys, FCM¶
kalshi.models.account ¶
Account-scoped models — API tier limits, etc.
RateLimit ¶
Bases: BaseModel
Per-direction (read/write) token-bucket rate limit.
The server enforces a token bucket per direction: bucket_capacity
tokens are allowed in a burst; refill_rate tokens are added per
second up to the cap. Requests above the cap return 429.
EndpointTokenCost ¶
Bases: BaseModel
Configured token cost for a single API endpoint.
AccountEndpointCosts ¶
Bases: BaseModel
Response from GET /account/endpoint_costs.
Lists API v2 endpoints whose configured token cost differs from
default_cost. Endpoints using the default are omitted.
AccountApiLimits ¶
Bases: BaseModel
Rate limits associated with the authenticated user's API tier.
NOTE: The published OpenAPI spec (v3.13.0) declares read_limit and
write_limit as ints, but the live server returns nested token-bucket
objects under read and write. The SDK matches the server. If the
spec is corrected upstream, the contract-drift test will flag it.
kalshi.models.subaccounts ¶
Subaccount models — multi-account workflows under one authenticated user.
CreateSubaccountResponse ¶
Bases: BaseModel
Response from POST /portfolio/subaccounts — the new subaccount number.
ApplySubaccountTransferRequest ¶
Bases: BaseModel
Body for POST /portfolio/subaccounts/transfer.
amount_cents is integer cents per spec (matches the buy_max_cost
convention on CreateOrderRequest). Pass 500 for $5.00, never
a Decimal. from_subaccount / to_subaccount use 0 for the
primary account and a positive integer for numbered subaccounts. The
server is the source of truth for the upper bound: spec describes
1-32 in prose but defines no JSON-schema maximum, and demo has
been observed allocating values above 32. The SDK validates only the
lower bound (ge=0) so server-assigned numbers always round-trip.
SubaccountBalance ¶
Bases: BaseModel
Balance for a single subaccount.
Note: updated_ts is a Unix seconds integer per spec
(format: int64), not an ISO datetime. RFQ/Quote timestamps are
format: date-time and surface as datetime; subaccount
timestamps follow the spec's int wire format. Callers wanting a
datetime can datetime.fromtimestamp(obj.updated_ts, tz=timezone.utc).
GetSubaccountBalancesResponse ¶
Bases: BaseModel
Response from GET /portfolio/subaccounts/balances.
SubaccountTransfer ¶
Bases: BaseModel
A past transfer between subaccounts.
created_ts is a Unix seconds integer per spec (format: int64),
matching SubaccountBalance.updated_ts. This is intentionally
different from RFQ/Quote timestamps, which are ISO datetime strings.
UpdateSubaccountNettingRequest ¶
Bases: BaseModel
Body for PUT /portfolio/subaccounts/netting.
SubaccountNettingConfig ¶
Bases: BaseModel
Netting state for a single subaccount.
GetSubaccountNettingResponse ¶
Bases: BaseModel
Response from GET /portfolio/subaccounts/netting.
kalshi.models.api_keys ¶
API Key models — programmatic credentials management.
API keys allow programmatic access without username/password. Each key
has a unique api_key_id and a user-provided name for bookkeeping.
create accepts a caller-supplied RSA public key (PEM); generate
has Kalshi mint a fresh key pair and returns the private key once
(store it — it cannot be retrieved again).
ApiKey ¶
Bases: BaseModel
An API key registered on the authenticated user's account.
scopes uses NullableList for the same reason envelope-level list
fields do — Kalshi has historically returned JSON null for required
list fields, and silently coercing None -> [] is safer than raising
a Pydantic ValidationError on a field most callers don't use.
GetApiKeysResponse ¶
Bases: BaseModel
Response from GET /api_keys.
api_keys uses NullableList since Kalshi has returned JSON null
for required list fields in other envelopes (see v0.9.0 Series fix).
Coercing None -> [] matches the envelope-list pattern established
across the rest of the SDK.
CreateApiKeyRequest ¶
Bases: BaseModel
Body for POST /api_keys — register a caller-supplied public key.
public_key must be a PEM-encoded RSA public key (i.e. starts
with -----BEGIN PUBLIC KEY-----). scopes defaults to full
access (["read", "write"]) server-side when omitted. If
"write" is included, "read" must be too.
CreateApiKeyResponse ¶
Bases: BaseModel
Response from POST /api_keys — the new key's ID.
GenerateApiKeyRequest ¶
Bases: BaseModel
Body for POST /api_keys/generate — let Kalshi mint a key pair.
GenerateApiKeyResponse ¶
Bases: BaseModel
Response from POST /api_keys/generate.
private_key is a PEM-encoded RSA private key returned ONLY in this
response. Store it securely — Kalshi does not retain a copy and it
cannot be retrieved later. The field is typed as :class:pydantic.SecretStr
so it prints as '**********' in repr()/logs; call
response.private_key.get_secret_value() to retrieve the PEM string
when you need to persist it.
Exchange, historical, search¶
kalshi.models.exchange ¶
Exchange-related models.
ExchangeStatus ¶
Bases: BaseModel
Current exchange operational status.
DailySchedule ¶
Bases: BaseModel
A single trading session within a day.
WeeklySchedule ¶
Bases: BaseModel
Weekly trading hours with per-day sessions.
MaintenanceWindow ¶
Bases: BaseModel
A scheduled maintenance window.
Schedule ¶
Bases: BaseModel
Exchange operating schedule.
Announcement ¶
Bases: BaseModel
An exchange-wide announcement.
UserDataTimestamp ¶
Bases: BaseModel
Timestamp when user-scoped exchange data was last validated.
Combine with WebSocket feeds for a live view — REST endpoints like
GetBalance/GetOrders/GetFills/GetPositions may lag the exchange by
a short delay; as_of_time is the upper bound on that lag.
kalshi.models.historical ¶
Historical data models.
RFQ / Quote, multivariate, live data, milestones, structured targets, incentive programs, order groups¶
kalshi.models.communications ¶
Communications / RFQ models — request-for-quote and quote subsystem.
UserFilterLiteral
module-attribute
¶
Filter for items created by the authenticated user. Spec: UserFilter enum.
MveSelectedLeg ¶
Bases: BaseModel
A selected leg within a multivariate event collection RFQ.
RFQ ¶
Bases: BaseModel
An RFQ — request for quote on a market.
Quote ¶
Bases: BaseModel
A quote responding to an RFQ.
GetCommunicationsIDResponse ¶
Bases: BaseModel
Wraps the caller's public communications ID.
GetRFQsResponse ¶
Bases: BaseModel
Paginated list of RFQs.
GetRFQResponse ¶
Bases: BaseModel
Single-RFQ envelope.
CreateRFQRequest ¶
Bases: BaseModel
Body for POST /communications/rfqs.
Spec allows contracts (integer) or contracts_fp (fixed-point string);
SDK commits to the integer form. Target cost uses _dollars wire suffix.
target_cost_centi_cents is deprecated upstream — omitted here.
Spec only requires market_ticker and rest_remainder; contracts
and target_cost are both optional and may both be omitted (a "shopping
around" RFQ with no committed size or cost). The SDK does not enforce a
contracts-or-cost invariant because the spec doesn't.
CreateRFQResponse ¶
Bases: BaseModel
Wraps the newly-created RFQ's id.
GetQuotesResponse ¶
Bases: BaseModel
Paginated list of quotes.
GetQuoteResponse ¶
Bases: BaseModel
Single-quote envelope.
CreateQuoteRequest ¶
Bases: BaseModel
Body for POST /communications/quotes.
Unlike order/amend requests, the spec names the wire fields yes_bid /
no_bid (no _dollars suffix) for this request. The Quote response
fields, however, use the _dollars suffix — handled on the response model.
CreateQuoteResponse ¶
Bases: BaseModel
Wraps the newly-created quote's id.
AcceptQuoteRequest ¶
Bases: BaseModel
Body for PUT /communications/quotes/{quote_id}/accept.
kalshi.models.multivariate ¶
Multivariate event collection models.
MultivariateCollectionStatusLiteral
module-attribute
¶
Multivariate collection status filter.
Spec: GetMultivariateEventCollections.status query enum.
AssociatedEvent ¶
Bases: BaseModel
An event associated with a multivariate collection.
MultivariateEventCollection ¶
Bases: BaseModel
A multivariate event collection (combo contract template).
TickerPair ¶
Bases: BaseModel
A market+event ticker pair with side, used in create/lookup request bodies.
Note: extra="allow" is intentional — the spec's TickerPair schema
has no additionalProperties: false, and some multivariate responses
echo back extra provider-specific keys. Because extra does not
inherit, request models that embed list[TickerPair] cannot rely on
their own extra="forbid" to reject phantom keys inside items. See
v0.9 TODO on nested-model drift coverage.
CreateMarketInMultivariateEventCollectionRequest ¶
Bases: BaseModel
Parameters for POST /multivariate_event_collections/{collection_ticker}.
Matches spec components.schemas.CreateMarketInMultivariateEventCollectionRequest.
Required: selected_markets. Optional: with_market_payload.
Carve-out: extra="forbid" on this model rejects unknown top-level
keys but NOT unknown keys inside each TickerPair in
selected_markets — TickerPair itself is extra="allow" (see
its docstring for why). Phantom keys nested inside a TickerPair
currently pass through to the wire. Tracked as a v0.9 follow-up.
See kalshi.resources.multivariate.MultivariateCollectionsResource.create_market
— v0.8.0 builds this model internally; method signature unchanged.
LookupTickersForMarketInMultivariateEventCollectionRequest ¶
Bases: BaseModel
Parameters for PUT /multivariate_event_collections/{collection_ticker}/lookup.
Matches spec
components.schemas.LookupTickersForMarketInMultivariateEventCollectionRequest.
Only selected_markets, required.
Carve-out: extra="forbid" on this model rejects unknown top-level
keys but NOT unknown keys inside each TickerPair in
selected_markets — TickerPair itself is extra="allow" (see
its docstring for why). Phantom keys nested inside a TickerPair
currently pass through to the wire. Tracked as a v0.9 follow-up.
See kalshi.resources.multivariate.MultivariateCollectionsResource.lookup_tickers
— v0.8.0 builds this model internally; method signature unchanged.
CreateMarketResponse ¶
Bases: BaseModel
Response from creating a market in a multivariate collection.
LookupTickersResponse ¶
Bases: BaseModel
Response from looking up tickers in a multivariate collection.
LookupPoint ¶
Bases: BaseModel
A point in the lookup history of a multivariate collection.
kalshi.models.live_data ¶
Live data models — real-time event state keyed by milestone.
LiveData.details is deliberately a loose dict[str, Any]: the spec
marks it additionalProperties: true with no fixed schema because the
shape varies per milestone type (e.g., football vs. political race).
GetGameStatsResponse.pbp (play-by-play) is similarly loose — each
period/event is a free-form object.
LiveData ¶
Bases: BaseModel
Live-data payload for a specific milestone.
GetLiveDataResponse ¶
Bases: BaseModel
Response from GET /live_data/milestone/{milestone_id}.
GetLiveDatasResponse ¶
Bases: BaseModel
Response from GET /live_data/batch — multiple milestones at once.
PlayByPlayPeriod ¶
Bases: BaseModel
A single period within a game's play-by-play.
events is a loose list of free-form objects (spec has no fixed
event schema) because each sport emits different event shapes.
NullableList coerces server-returned null to [] (Kalshi has
historically sent null for required list fields).
PlayByPlay ¶
Bases: BaseModel
Play-by-play data organized by period.
GetGameStatsResponse ¶
Bases: BaseModel
Response from GET /live_data/milestone/{milestone_id}/game_stats.
pbp is None for unsupported milestone types or milestones
without a Sportradar ID (spec: "Returns null for unsupported milestone
types or milestones without a Sportradar ID").
kalshi.models.milestones ¶
Milestone models — structured markers for sports/elections/crypto events.
Milestones anchor live-data feeds (see :mod:kalshi.models.live_data).
Each milestone has a type (e.g., football_game, political_race)
that determines what the details object contains. last_updated_ts
and start_date are ISO date-time strings per spec (unlike
subaccount timestamps which are Unix ints).
Milestone ¶
Bases: BaseModel
A structured event milestone (game, race, tournament, etc.).
GetMilestoneResponse ¶
Bases: BaseModel
Response from GET /milestones/{milestone_id}.
GetMilestonesResponse ¶
Bases: BaseModel
Response from GET /milestones — paginated list.
milestones uses NullableList since Kalshi has returned JSON null
for required list fields in other envelopes (see the v0.9.0 Series
fix). Coercing None -> [] here matches the pattern established on
Milestone.related_event_tickers and primary_event_tickers.
kalshi.models.structured_targets ¶
Structured targets — external entities (players, teams, etc.) markets anchor to.
Unlike most spec schemas, every StructuredTarget field is optional per
spec — Kalshi returns partial records depending on the target type. The
SDK mirrors that: all fields default to None / {}.
details is flexible JSON (additionalProperties: true) whose keys
depend on type (e.g., a basketball_player carries different keys
than a horse_race_entry). Consumers are expected to branch on type.
StructuredTarget ¶
Bases: BaseModel
An external entity a market can be structured against.
target_type is exposed under the SDK-local name (spec wire key
is type) to avoid shadowing the Python built-in — same convention
as the SDK's query-param renames on this and related resources.
GetStructuredTargetsResponse ¶
Bases: BaseModel
Paginated response for GET /structured_targets.
GetStructuredTargetResponse ¶
Bases: BaseModel
Response for GET /structured_targets/{id}.
kalshi.models.incentive_programs ¶
Incentive programs — market-level liquidity/volume reward programs.
Note on period_reward: spec says "Total reward for the period in
centi-cents". The SDK exposes it as a plain int (caller converts to
dollars by dividing by 10 000). Fractional values come through
target_size_fp (FixedPointCount string).
IncentiveProgramStatusLiteral
module-attribute
¶
Incentive program status filter. Spec: GetIncentivePrograms.status query enum.
IncentiveProgramTypeLiteral
module-attribute
¶
Incentive program type filter. Spec: GetIncentivePrograms.type query enum.
IncentiveProgram ¶
Bases: BaseModel
A single incentive program rewarding liquidity or volume on a market.
GetIncentiveProgramsResponse ¶
Bases: BaseModel
Paginated response for GET /incentive_programs.
Uses next_cursor (not cursor) as the pagination key — unique
to this endpoint.
kalshi.models.order_groups ¶
Order Groups models — rolling 15-second contracts-limit groups for linked orders.
OrderGroup ¶
Bases: BaseModel
A single order group (list response entry).
GetOrderGroupResponse ¶
Bases: BaseModel
Single-group response — omits id (path param), adds member order IDs.
CreateOrderGroupResponse ¶
Bases: BaseModel
Create response — wraps the new group's id.
CreateOrderGroupRequest ¶
Bases: BaseModel
Create body. SDK sends integer form; spec's contracts_limit_fp string variant unused.
UpdateOrderGroupLimitRequest ¶
Bases: BaseModel
Update-limit body. No subaccount — spec omits SubaccountQuery on /limit.
WebSocket¶
See WebSocket for the narrative version.
kalshi.ws.client.KalshiWebSocket ¶
KalshiWebSocket(
auth: KalshiAuth,
config: KalshiConfig,
heartbeat_timeout: float = 30.0,
on_state_change: _StateChangeCb | None = None,
on_error: (
Callable[[ErrorMessage], Awaitable[None]] | None
) = None,
)
WebSocket client for real-time Kalshi market data.
Usage::
ws = KalshiWebSocket(auth=auth, config=config)
async with ws.connect() as session:
async for msg in session.subscribe_ticker(tickers=["ECON-GDP-25Q1"]):
print(msg.msg.yes_bid)
on ¶
Decorator to register a callback for a channel.
Works both before and after connect(). Callbacks registered before connect are buffered and applied when the session starts.
run_forever
async
¶
Block until the recv loop terminates. Use with the callback API.
Requires at least one prior subscribe_* (or generic
:meth:subscribe) call in the same session — the recv loop is
started lazily by the subscribe machinery, and without it there
is nothing to drain. Registering an @ws.on(channel) callback
does NOT subscribe; the server only sends frames for channels you
have explicitly subscribed to, so a callback without a matching
subscribe sees nothing.
:raises KalshiSubscriptionError: run_forever() was called
before any subscribe_* request landed (formerly a silent
no-op return — fixed in #175).
orderbook
async
¶
Subscribe to orderbook_delta and yield full Orderbook on each update.
kalshi.ws.connection.ConnectionState ¶
Bases: Enum
WebSocket connection lifecycle states.
kalshi.ws.backpressure.OverflowStrategy ¶
Bases: Enum
What to do when the message queue is full.
kalshi.ws.backpressure.MessageQueue ¶
Bases: Generic[T]
Bounded async queue with configurable overflow behavior.
Implements AsyncIterator so consumers can async for msg in queue.
Iteration stops when a sentinel is pushed (graceful shutdown).
kalshi.ws.models ¶
WebSocket message models for all 11 Kalshi channels.
BaseMessage ¶
Bases: BaseModel
Base for all WebSocket messages.
ErrorMessage ¶
Bases: BaseModel
Error response from the server.
ErrorPayload ¶
Bases: BaseModel
Error message payload.
OkMessage ¶
Bases: BaseModel
Generic success response (list_subscriptions, update_subscription).
SubscribedMessage ¶
Bases: BaseModel
Response to a subscribe command.
SubscriptionInfo ¶
Bases: BaseModel
Subscription confirmation payload.
UnsubscribedMessage ¶
Bases: BaseModel
Response to an unsubscribe command.
CommunicationsMessage ¶
Bases: BaseModel
RFQ and quote notifications. Payload varies by sub-type, so msg is a dict.
Users who want typed parsing can use the individual payload models (RfqCreatedPayload, QuoteCreatedPayload, etc.) to validate msg contents.
QuoteAcceptedPayload ¶
Bases: BaseModel
Quote accepted notification payload.
QuoteCreatedPayload ¶
Bases: BaseModel
Quote created notification payload.
QuoteExecutedPayload ¶
Bases: BaseModel
Quote executed notification payload.
RfqCreatedPayload ¶
Bases: BaseModel
RFQ created notification payload.
Wire format per AsyncAPI spec: created_ts is an RFC3339 date-time
string; target_cost_dollars is a dollar string; contracts_fp is a
2-decimal fixed-point count string.
TODO(v0.15.1): created_ts is spec-aligned but lacks live capture.
The v0.14.0 user_orders capture showed demo emitting
created_ts_ms as integer milliseconds instead of the spec's ISO
string. If Communications follows the same pattern, created_ts: str
will reject frames that previously parsed as int. Capture a live
frame on demo when the channel is active and confirm or adjust.
extra="allow" provides a soft landing for unexpected extras.
RfqDeletedPayload ¶
Bases: BaseModel
RFQ deleted notification payload.
deleted_ts is an RFC3339 date-time string per AsyncAPI spec.
Same created_ts_ms-precedent caveat as :class:RfqCreatedPayload.
FillMessage ¶
Bases: BaseModel
Fill update message. NO required seq.
FillPayload ¶
Bases: BaseModel
Payload for fill messages (private channel).
Wire format per AsyncAPI spec: yes_price_dollars is a dollar-decimal
string; count_fp / post_position_fp are fixed-point count strings;
fee_cost is a dollar-decimal string; ts is an integer Unix
timestamp (seconds).
MarketLifecycleMessage ¶
Bases: BaseModel
Market lifecycle v2 update message. NO required seq.
MarketLifecyclePayload ¶
Bases: BaseModel
Payload for market_lifecycle_v2 messages (public channel).
Discriminated by event_type field. Conditional fields depend on event_type: - created/activated: open_ts, close_ts, title, subtitle, series_ticker - determined: result, determination_ts - settled: settlement_value, settled_ts - deactivated: is_deactivated
MarketPositionsMessage ¶
Bases: BaseModel
Market positions update message. NO required seq.
MarketPositionsPayload ¶
Bases: BaseModel
Payload for market_positions messages (private channel).
Dollar-denominated fields use :data:DollarDecimal per the CLAUDE.md
convention. position is a fixed-point contract count (string).
MultivariateLifecycleMessage ¶
Bases: BaseModel
Multivariate market lifecycle message. Same payload as MarketLifecycleMessage.
MultivariateMessage ¶
Bases: BaseModel
Multivariate update message. NO required seq.
MultivariatePayload ¶
Bases: BaseModel
Payload for multivariate messages (public channel).
SelectedMarket ¶
Bases: BaseModel
A selected market within a multivariate collection.
OrderGroupMessage ¶
Bases: BaseModel
Order group update message. HAS required seq (one of only 2 channels).
OrderGroupPayload ¶
Bases: BaseModel
Payload for order_group_updates messages (private channel).
OrderbookDeltaMessage ¶
Bases: BaseModel
Incremental orderbook update.
OrderbookDeltaPayload ¶
Bases: BaseModel
Payload for orderbook_delta messages.
Wire format per AsyncAPI spec and confirmed on demo: price_dollars is
a dollar-decimal string (e.g. "0.0200"), delta_fp is a fixed-point
count string (e.g. "10.00", may be negative), ts is an RFC3339
timestamp string (e.g. "2026-04-19T18:43:37.662364Z").
OrderbookSnapshotMessage ¶
Bases: BaseModel
Full orderbook snapshot, sent on initial subscribe.
OrderbookSnapshotPayload ¶
Bases: BaseModel
Payload for orderbook_snapshot messages.
Wire format per AsyncAPI spec: yes_dollars_fp and no_dollars_fp are
arrays of [price_in_dollars, contract_count_fp] string pairs. Each row
is two JSON strings, e.g. ["0.5500", "100.00"]. Consumers should parse
both elements as :class:decimal.Decimal.
TickerMessage ¶
Bases: BaseModel
Ticker update message. NO required seq.
TickerPayload ¶
Bases: BaseModel
Payload for ticker messages (public channel).
Wire format per AsyncAPI spec: yes_bid_dollars / yes_ask_dollars are
dollar-decimal strings; ts is an integer Unix timestamp (seconds);
_fp fields are fixed-point count strings.
TradeMessage ¶
Bases: BaseModel
Trade update message. NO required seq.
TradePayload ¶
Bases: BaseModel
Payload for trade messages (public channel).
Wire format per AsyncAPI spec: yes_price_dollars / no_price_dollars
are dollar-decimal strings; count_fp is a fixed-point count string;
ts is an integer Unix timestamp (seconds).
UserOrdersMessage ¶
Bases: BaseModel
User orders update message. NO required seq.
UserOrdersPayload ¶
Bases: BaseModel
Payload for user_orders messages (private channel).
Wire format captured on demo 2026-04-19: yes_price_dollars is a dollar
string with up to 4 decimals ("0.0100"); *_fp counts are 2-decimal
fixed-point strings; taker_fill_cost_dollars / maker_fill_cost_dollars
/ taker_fees_dollars / maker_fees_dollars are dollar strings with
up to 6 decimals. All stored as :class:decimal.Decimal via
:data:DollarDecimal.
Testing helpers¶
See Testing for the narrative version.
kalshi.testing ¶
Record/replay mock layer for testing against the Kalshi SDK.
Drop a :class:RecordingTransport (or :class:AsyncRecordingTransport) into the
client once to capture real API calls to disk, then swap in
:class:ReplayTransport (or :class:AsyncReplayTransport) to run tests offline.
Requests are matched by HTTP method + URL path + sorted query parameters. The
KALSHI-ACCESS-SIGNATURE and KALSHI-ACCESS-TIMESTAMP headers are ignored,
so signature drift between record and replay does not cause misses.
.. warning::
Recorded fixtures contain the full response body returned by Kalshi. If you
record against an account with real funds you will write balances, positions,
order history, and any PII the API returns to disk. **Always ``.gitignore``
the fixture directory unless you have manually scrubbed the JSON, and prefer
recording against the demo environment whenever possible.**
Usage::
from pathlib import Path
from kalshi import KalshiClient
from kalshi.testing import RecordingTransport, ReplayTransport
# Record once against the real API:
with KalshiClient.from_env(transport=RecordingTransport(Path("fixtures"))) as c:
c.exchange.status()
# Replay in tests, no network:
with KalshiClient(transport=ReplayTransport(Path("fixtures"))) as c:
c.exchange.status() # served from fixtures/GET_trade-api_v2_exchange_status.json
AsyncRecordingTransport ¶
AsyncRecordingTransport(
dir_path: str | Path,
*,
real_transport: AsyncBaseTransport | None = None
)
Bases: AsyncBaseTransport
Async equivalent of :class:RecordingTransport.
RecordingTransport ¶
Bases: BaseTransport
Sync httpx transport that proxies real requests and records each pair to disk.
Wraps any underlying httpx.BaseTransport (defaults to a fresh
httpx.HTTPTransport) so real network calls go through, while every
request/response pair is buffered in memory and flushed to disk on
close() — see #105 for the prior O(N²) per-request rewrite this fixes.
AsyncReplayTransport ¶
Bases: AsyncBaseTransport
Async equivalent of :class:ReplayTransport.
FixtureNotFoundError ¶
Bases: LookupError
Raised when no recorded fixture matches an incoming request.
ReplayTransport ¶
Bases: BaseTransport
Sync httpx transport that serves responses from a directory of fixtures.
Reads JSON fixture files written by :class:RecordingTransport. Requests are
matched by HTTP method, URL path, and sorted query parameters (body and auth
headers are ignored).
Raises :class:FixtureNotFoundError when no matching fixture exists — never
returns a synthetic 404 silently.