Kalshi API Balance, Positions, Fills and Settlements Explained

DeskBot — trading software and customer guides

Placing an order through the Kalshi API is the easy part. The harder part is knowing afterwards what actually happened: how many contracts filled, at what price, what the fee was, what you hold now, and what each market paid when it settled. Kalshi's portfolio endpoints answer each of those questions. This guide walks through the four a trading bot reads most (balance, positions, fills and settlements), the units each field uses, and a worked example that reconciles one trade from order to payout.

Short answer

  • GET /portfolio/balance: cash you can trade with and the value of your open positions. balance and portfolio_value are in cents; balance_dollars is a dollar string.
  • GET /portfolio/positions: what you hold in each market. Positive position_fp means YES contracts, negative means NO. It returns unsettled positions by default.
  • GET /portfolio/fills: every match on your orders, with price, count, fee and whether you were the taker.
  • GET /portfolio/settlements: what each finished market paid you. revenue is in cents, and a winning contract pays 100 cents.
  • All four need a signed request with your API key. Older records move to the /historical endpoints.

Before you start: base URL and signing

Kalshi's API reference lists https://external-api.kalshi.com/trade-api/v2 as the production Trade API server, with https://api.elections.kalshi.com/trade-api/v2 as a shared server it still supports. The demo environment has matching servers on kalshi.co. Market data can be read without an account, but every /portfolio endpoint is private. Each request carries three headers: your API key ID, a timestamp in milliseconds, and a signature made with your private key. We cover that setup in the Kalshi API Python tutorial and the demo environment guide. Practise these calls in demo first, where a mistake costs nothing.

One more thing to know up front: Kalshi has been moving its responses to fixed-point fields. Counts arrive as strings ending in _fp (for example count_fp), and dollar amounts arrive as strings ending in _dollars. A few older fields are still integers in cents. Parse each field by its documented type rather than guessing, and keep your money math in integer cents or a decimal type, never floating point.

1. Balance: what you can trade with

GET /portfolio/balance returns:

Field Unit Meaning
balance cents (integer) Available balance
balance_dollars dollar string The same balance, as fixed-point dollars
portfolio_value cents (integer) Value of your positions
updated_ts Unix timestamp When the balance last changed

Kalshi notes that both values include every exchange index unless you pass exchange_index, and that a subaccount parameter reads a subaccount instead of the primary account. The endpoint also accepts API keys limited to the read::portfolio_balance scope, which is handy for a dashboard that should never be able to trade.

A bot uses the balance for one thing above all: deciding whether it can afford the next order. If the cost of an order plus its fee is more than balance, the order will be rejected, so check first and skip cleanly.

2. Positions: what you hold right now

GET /portfolio/positions returns two lists, market_positions and event_positions, plus a cursor for paging. The per-market fields a bot reads most:

Field Meaning
ticker The market, for example a KXBTC15M-... window
position_fp Contracts held. Positive is YES, negative is NO
market_exposure_dollars Cost of the position
total_traded_dollars Total spent in this market
realized_pnl_dollars Profit or loss already locked in
fees_paid_dollars Fees on your fills in this market

By default the endpoint returns only unsettled positions. Pass settlement_status=settled to page through settled ones still in the live data set, or settlement_status=all for both. You can filter by ticker or by a single event_ticker. Page size defaults to 100 and goes up to 1,000.

The sign convention matters. A bot that reads position_fp = "-5" holds five NO contracts, not "minus five" of anything. Treating the sign as a direction keeps you from doubling a position you already have, which is the most common bookkeeping bug in home-made bots.

3. Fills: every match on your orders

An order and a fill are different things. One order for 10 contracts can fill in three pieces at different moments, or partly fill and leave the rest resting. GET /portfolio/fills lists each match. Useful fields:

  • fill_id and order_id: which fill, and which of your orders it belongs to. Filter by order_id to see exactly how one order filled.
  • ticker, count_fp, yes_price_dollars and no_price_dollars: where, how many, and at what price.
  • outcome_side: the side the fill positioned you for. Kalshi's docs say buy-yes and sell-no produce yes; buy-no and sell-yes produce no. book_side carries the same information as bid or ask.
  • is_taker: true if your order took liquidity from the book. Taker and maker fills can carry different fees.
  • fee_cost: the fee on this fill, in fixed-point dollars.
  • created_time: when it filled.

You can narrow the list with min_ts and max_ts (Unix timestamps). Fills from before Kalshi's historical cutoff are only available through GET /historical/fills, so a reconciliation script that looks back far enough has to read both. The older side, action, trade_id and market_ticker fields are still present for compatibility; Kalshi's order direction guide explains how to move to the newer fields.

4. Settlements: what each market paid

When a market resolves, GET /portfolio/settlements records what you held and what you were paid:

  • market_result: yes, no or scalar.
  • yes_count_fp and no_count_fp: contracts you held on each side at settlement.
  • yes_total_cost_dollars and no_total_cost_dollars: your cost basis on each side.
  • revenue: what the settlement paid, in cents. Winning contracts pay 100 cents each.
  • fee_cost: total fees, in fixed-point dollars.
  • settled_time: when payouts were processed.

Note the mixed units: revenue is an integer in cents while costs and fees are dollar strings. Convert everything to one unit before you subtract. This endpoint returns only records still in the live data set; archived ones are read through GET /historical/positions, which has different fields.

Worked example: reconciling one trade

Say a bot buys 10 YES contracts at 40¢ in a Bitcoin 15-minute market and the whole order fills as a taker.

The fill. The fill shows count_fp 10, yes_price_dollars 0.40, is_taker true and outcome_side yes. Kalshi's general taker fee formula is 0.07 × contracts × price × (1 − price), rounded up: 0.07 × 10 × 0.40 × 0.60 = $0.168. The fill's fee_cost should be about $0.1680. Check the actual fee schedule for each series, because fees can differ by market and change over time.

The cost. 10 × $0.40 = $4.00 for the contracts, plus $0.168 in fees: $4.168 in total. Your balance drops by about that much, and GET /portfolio/positions now shows position_fp 10 for that ticker.

If the market settles YES. The settlement record shows market_result yes, yes_count_fp 10 and revenue 1000 (cents), which is $10.00. Net result: $10.00 − $4.00 − $0.168 = $5.832.

If it settles NO. revenue is 0. Net result: −$4.168.

The breakeven. To come out even over many trades like this one, the YES side has to win $4.168 ÷ $10.00 = 41.68% of the time, not 40%. The fee moves the bar up by about 1.7 points. That is why the fee field on every fill matters: a strategy that looks even before fees can lose after them. Our fees and breakeven guide works this through at other prices.

A note on rounding

Kalshi's fee rounding page explains that fees are computed to six decimal places, and that balances are aligned to $0.0001 for direct members and $0.01 for members who trade through a broker (FCM). When a fill would leave your balance between those steps, a small rounding fee brings it back, and overpaid rounding is refunded later in the same order. So a reconciliation that matches to the cent can still be a hundredth of a cent off. Allow for that, rather than treating it as a bug.

A simple reconciliation loop

  1. When the bot places an order, store the order_id it gets back.
  2. Read GET /portfolio/fills?order_id=... to see what actually filled, at what price and fee. Do not assume the full count filled.
  3. Compare GET /portfolio/positions for that ticker with the sum of your fills. If they disagree, stop placing new orders in that market until you know why.
  4. After the window closes, read GET /portfolio/settlements?ticker=... and record revenue, cost and fees for that trade.
  5. Once a day, compare balance with what your records say it should be.

Every list endpoint pages with cursor. Keep requesting until the cursor comes back empty, or you will miss records on a busy day. Portfolio calls count against the same rate limits as everything else; see our rate limits and WebSocket guide for ways to cut polling, such as listening for fills on a WebSocket instead of asking every second.

Common mistakes

  • Mixing cents and dollars. revenue and balance are cents; most other money fields are dollar strings.
  • Using floats for money. Fixed-point strings exist so you do not lose fractions of a cent. Parse them into a decimal type.
  • Reading only unsettled positions. A position disappears from the default view once its market settles. Look it up in settlements instead.
  • Ignoring partial fills. An order can fill 3 of 10 contracts and rest the other 7 until the market closes.
  • Forgetting the historical endpoints. Old fills and positions move out of the live endpoints after Kalshi's cutoff.

Where DeskBot fits

DeskBot uses proprietary trading logic with customer-selected settings. Its dashboard shows supported market views and app activity. See the current product and support pages for the features and requirements of your release.

More on the 15-minute markets: close times and settlement, Bitcoin 15-minute results data, Kalshi trading bot comparison, is a Kalshi trading bot worth it? and Windows vs Mac setup.

How do I get my Kalshi balance through the API?

Send a signed request to GET /portfolio/balance. balance is your available balance in cents, balance_dollars is the same value as a dollar string, and portfolio_value is the value of your positions in cents.

What does a negative position mean in the Kalshi API?

In GET /portfolio/positions, a negative position_fp means you hold NO contracts and a positive one means YES contracts.

Why is my settled position missing from /portfolio/positions?

The endpoint returns unsettled positions by default. Pass settlement_status=settled or all, or read GET /portfolio/settlements. Archived records are in GET /historical/positions.

What is the difference between an order and a fill on Kalshi?

An order is your instruction to trade. A fill is a match against it. One order can produce several fills, or partly fill and rest the remainder.

Is settlement revenue in dollars or cents?

Cents. A winning contract pays 100 cents, so 10 winning contracts show revenue 1000. Fees and cost basis on the same record are dollar strings.

Is DeskBot affiliated with Kalshi?

No. DeskBot is independent software and is not affiliated with, endorsed by or sponsored by Kalshi.

Risk disclaimer: Trading involves risk of loss. Only trade money you can afford to lose. DeskBot does not guarantee profits, returns, win rates or income. Endpoint names and fields are as published in Kalshi's API documentation on 27 October 2026 and can change; check Kalshi's documentation before relying on them. The worked example is illustrative arithmetic, not a result. Nothing on this page is financial advice. DeskBot is not affiliated with Kalshi.

Current product information: Kalshi · Robinhood · Setup and support. Check the relevant product page for current downloads, pricing, renewal and cancellation terms. Desktop and mobile products have separate availability and offers.

Educational content, not financial advice. Trading can lose money. DeskBot does not guarantee profits, returns, win rates or income and is not affiliated with or endorsed by the named exchanges or brokers.