trade capability.
Every endpoint is listed under Kalshi in
the API reference. @parlayx/sdk and Python parlayx reach all of them
through client.kalshi.
Addressing a market
ticker names the Kalshi market. A ticker is uppercase alphanumeric segments
joined by -, with . allowed inside a segment for a decimal strike:
KXPRES-2028 and KXBTCD-26AUG0607-T64599.99 are both well formed.
side picks the direction on that market’s YES book. BID buys YES contracts
and profits if the market resolves YES. ASK sells them and profits if it
resolves NO. Selling YES and buying NO are the same order on Kalshi, so ASK
is how you do either.
A Kalshi listing from market discovery carries bookSide, the side that buys
its outcome.
A well-formed ticker naming a market the venue cannot resolve comes back
404 NOT_FOUND.
Sizing and pricing
count is contracts and price is dollars per contract, both decimal strings.
{ "count": "10", "price": "0.38" } bids for ten contracts at 38 cents each.
Contracts trade in steps of 0.01, so "2.50" is a valid size. Prices go as fine
as $0.0001, but each market sets its own grid of valid prices, and many quote
only whole cents. A price off the market’s grid is refused with 400, and never
moved to the nearest price the market does take.
price is always on the market’s YES book, whichever direction you are trading.
To buy NO at 40 cents, send an ASK at "0.60"; to sell NO at 40 cents, send a
BID at the same price.
If you are coming from the previous side and action pair, the four
combinations map onto the two directions like this:
Every Kalshi order is a limit order. There is no order type field. An immediate
execution policy together with an aggressive price is how you ask for a market
order.
Execution policy
Three further controls shape a resting order.
postOnly: true refuses the
order if it would cross the book instead of resting as a maker.
cancelOrderOnPause: true cancels the order if the exchange pauses trading
instead of leaving it resting. selfTradePrevention chooses which order is
cancelled when your order would trade against another order from the same
Kalshi account.
selfTradePrevention defaults to TAKER_AT_CROSS.
Submitting
A 201 means the order was accepted, not that the venue holds it. Thestatus it
returns is the order’s recorded state when the response was produced, so read the
order back to learn whether it rested or filled. A retry under the same idempotency
key returns the order it already created, carrying whatever state that order has
now. Read its fills for execution.
filledCount and remainingCount report what the venue said when it acknowledged
the order: contracts filled at that instant, and contracts left resting. Both read
null when there is no such acknowledgement to report, which covers a replay of an
order placed earlier and an outcome established by reading the order back, and when
the venue stated no count. Null is not zero, so treat it as unknown and read the
order for its current state.
averagePrice is what those filled contracts averaged, and it reads null when
nothing filled. It is on the market’s YES book, like the price you sent, so nothing
stands between the two.
All three are decimal strings. Kalshi reports contract counts to a hundredth, a fill can be
fractional even when the order size was whole, and an average over fills at "0.38" and
"0.39" is "0.384" whatever limit you set. Parse them as decimals.
Retry an unresolved submission with the same Idempotency-Key and the same
body. A different key creates a different order.
Batch Submit Orders places up to 20
orders in one request, sent as { orders: [order, ...] } under one
Idempotency-Key header that covers the whole batch. Resending the identical
batch under the same key reaches the same orders and places nothing twice. Each
order gets its own outcome, so one refused order does not fail the others. A
200 means the batch was read, not that every order was placed, so check each
element of orders. Element i answers request entry i, including an entry
refused before submission, and carries the clientOrderId you sent (null if you
sent none or the entry was invalid). Each element is one flat record, the way
the venue’s own batch receipt is: orderId, status, filledCount,
remainingCount and averagePrice beside error. Read error first:
Never resend a batch under a new
Idempotency-Key, which places its orders a
second time. The key covers every order you sent, including the ones refused as
invalid. A key reused with a different batch is rejected with
422 IDEMPOTENCY_CONFLICT, and a missing or malformed key with 400.
To fix a refused order, send only the corrected order as a new batch under a new
key. Resending the whole batch under a new key places the accepted orders again,
and any of them that carries a clientOrderId is refused as already in use
rather than placed twice.
clientOrderId is your own name for an order: at most 64 characters of letters,
digits, or - _ : / + =. It is returned unchanged on every read of the
order and its fills. An order placed without one returns its orderId there
instead.
Each value may be used once and is never freed, including after the order carrying
it is filled or cancelled. A second order under a label already used is rejected
with 400 INVALID_REQUEST.
The label reaches Kalshi, so it must be unique across everything trading under the
same Kalshi account, not only across your own orders. A label another trader on
that account has used is unavailable to you. Where several traders share an
account, give each one its own prefix, or use a generated id.
clientOrderId and Idempotency-Key answer different questions. The key
answers whether this is the same request. The label answers which of your own
quotes an order is.4xx: 400 INSUFFICIENT_BALANCE for
an order larger than your available balance, 404 NOT_FOUND for an unknown or
expired market, and 400 INVALID_REQUEST for anything else, with the venue’s
own explanation in message. A body carrying a field the request does not
define is rejected rather than stripped.
Kalshi holds a pod’s cash separately for each Market Group, and an order can
only use the cash in its market’s group. If that group has too little, the order
is refused with a message naming the group. An admin can move cash into the
group from Transfer Funds on the Platform.
See Kalshi’s exchange sharding guide.
Changing an order
Amend Order reprices or resizes a resting order in place, keeping its id. Send the terms you want the order to hold, not the change you want applied. An amend to the terms the order already holds answers200 with the order
unchanged. Both responses carry previous, the price and count the order held
before the call.
Decrease Order reduces a resting order
to an absolute remainingCount. A remainingCount naming more contracts than
the order has resting is refused with 400, and so is 0. Cancel the order
instead.
Either route answers 409 when the order cannot be changed from its current
state. A 503 means the venue’s outcome is unknown, and an amend can cross and
fill, so read the order before retrying.
Cancelling
Cancel Order cancels by the issued order id. A409 carrying ORDER_NOT_CANCELLABLE means the order cannot be
cancelled from its current state, most often because it is still being placed
and has no venue order yet. Read the order and retry once it reports a
venueOrderId.
A 503 means the cancel did not complete. It carries INTERNAL_ERROR, the
same code a 500 carries, so branch on the status rather than the code, and
bound any retry rather than repeating indefinitely.
Cancel Orders cancels the eligible
orders placed by the signing key’s owner, optionally narrowed with ticker.
cancelled lists the orders whose remaining quantity was confirmed cancelled,
and notCancelled names each of the rest with a reason:
When
nextPageToken is present, send it back with the same ticker to
continue through the orders the request did not reach.
Reading orders and fills
Orders and fills reportyesPrice and noPrice in dollars per contract.
noPrice is exactly one minus yesPrice. Orders also report
averageYesPrice and averageNoPrice, both null before any fill.
outcomeSide names the order’s economic direction: YES for BID, NO
for ASK. Fills carry their parent order’s direction. An ASK can close an
existing YES holding, so this field does not describe the resulting position.
side remains the book direction and the order request is unchanged.
The read fields price and averagePrice are deprecated aliases for
yesPrice and averageYesPrice. They retain their existing values.
An order placed with an immediate policy, IOC or FOK, reports SUBMITTED
while it is live at the venue rather than OPEN. It has no remainder that can
rest, so it is not cancellable. OPEN means resting and cancellable.
filledCount, averagePrice, feesCents and feesUsd read null before
the first fill. A submit response reports the counts at acknowledgement instead
(see Submitting), so an order that has not filled can read 0 there
and null on a read. feesCents is a rounded render of feesUsd and the two round
independently, so summing an order’s fills can differ from the order’s own total
by a cent. Reconcile on feesUsd.
Get Order Fills returns the fills
oldest first, each with its own count, price and fee.
queuePosition on Get Order is a
diagnostic: a decimal string counting the Kalshi contracts ahead of the order
in price-time priority, with "0.00" meaning none. It is a snapshot, and
guarantees neither execution order nor fill timing.
List Order Queue Positions
returns the same figure for a page of your resting orders in one call, up to 100
at a time.
null, or an absent field, means unknown rather than none. Order list, amend,
decrease and cancel responses return null, a submit response omits the field,
and the other venues’ order views return null too.Positions and balance
Positions reportaverageYesPrice, averageNoPrice, yesMark and noMark
as decimal dollar strings. Each NO price is exactly one minus its YES price,
and is null when the corresponding YES price is unavailable. averagePrice
and mark are deprecated aliases for averageYesPrice and yesMark, with
unchanged values. Signed contracts, valueUsd and unrealizedPnlUsd retain
their existing meaning.
List Positions keys a position by
ticker alone, because Kalshi nets a market into one holding: buying the no side
offsets a yes holding rather than opening a second position. contracts is a
decimal string on the market’s yes side, and its sign is what says which way the
holding leans. Positive is long yes; negative is long no, which is the same
holding as short yes. It is a decimal because contracts trade in steps of 0.01,
so a holding of "2.63" is ordinary.
averagePrice and mark are dollar strings from 0 to 1 on that same yes side,
the same frame you submit a price in. They are not rounded to a whole cent:
Kalshi sets its tick size per market and bands it within one market, so many
markets quote in tenths of a cent and a whole-cent figure is not a price those
books can take. averagePrice reads null when the holding has no single entry
price, and mark reads null when no fresh price is available. valueUsd and
unrealizedPnlUsd are dollar strings composed from those two figures, and read
null when a price they need is null.
The listing carries open rows only. A market that settles drops out of it
rather than turning settled, so a row that disappears is not a loss.
On Get Balance, available is the account
cash you can place orders against, in USD, with cash committed to your resting
buy orders already excluded. Kalshi’s own reported balance still contains that
cash, so sizing against the venue’s figure oversizes.