Skip to main content
Kalshi is an account venue: an admin connects the venue credential at the organization and ties it to a pod, and traders in that pod trade through the tie. Submitting, amending, decreasing or cancelling an order needs a key with the 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.
Tickers are case sensitive and are not upcased for you. A ticker that does not match is refused with 400 INVALID_REQUEST and no order is created.
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.
MAKER names the order that is cancelled, not the one that is favoured. It removes your own resting quote. If the resting order is the one you want to keep, use TAKER_AT_CROSS, which cancels the incoming order instead.

Submitting

A 201 means the order was accepted, not that the venue holds it. The status 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.
A refusal you can act on comes back as a 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.
count on an amend is the order’s already filled count plus the resting count you want after the amend, not the resting count on its own. An order that has filled 4 of 10 and should keep 2 contracts resting takes count: 6.
An amend to the terms the order already holds answers 200 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. A 409 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 report yesPrice 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 report averageYesPrice, 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.
A balance that could not be read fresh from the venue returns 503. Do not treat that response as zero. A 404 means the pod has no Kalshi account connected, or its connection needs reconnecting.