API

Get Started

WelcomeQuickstartGuidesChangelogSDKs and ToolsAdditional InfoFAQ

Authorization

POSTCreate personal access token

List Accounts

GETGet accounts

Account Details

GETGet account portfolio v2GETGet history

Tax Lot Selling

GETGet unrealized tax lotsGETGet unrealized tax lots for symbolGETGet unrealized tax lots csv

Instrument Details

GETGet all instrumentsGETGet instrumentGETSearch bonds

Market Data

POSTGet quotesGETGet bond detailsPOSTGet option expirationsPOSTGet option chainGETGet bars v2GETGet bars v2 with aggregation

Orders

POSTPreflight single legPOSTPreflight multi legPOSTPlace orderPUTReplace orderPOSTSearch ordersPOSTPlace multileg orderGETGet orderDELETECancel order

Option Details

GETGet option greeksPOSTGet strategy quote

Event Contracts

POSTGet event summaryGETGet event categoriesGETGet event details
HelpFeedback

Get Started

Trading prediction markets

Learn how to browse prediction markets, read an event's outcomes and contract pricing, pull quotes and price history, and place your first event contract order.

How prediction markets are structured

A prediction market is a question about a future event, such as "Fed decision in Oct 2026?". The API models it in three levels:

  • An event is the question itself, identified by an eventSymbol such as KALSHI.KXFEDDECISION-26OCT.
  • Each event has one or more outcomes, such as "Fed maintains rate" or "Cut 25bps". Each outcome has its own rules, trading timeline, and state.
  • Each outcome has two tradable event contracts: a YES contract (symbol ending in .Y) and a NO contract (symbol ending in .N).

Contracts are priced in dollars between 0.00 and 1.00. A contract that settles in your favor pays $1.00, and one that settles against you pays $0.00, so the price is the market's implied probability of that result. A YES contract trading at 0.80 means the market puts the outcome at roughly 80%.

Example: a hypothetical trade

Suppose an event asks "Will it snow in Central Park on December 25?" The YES contract has an ask of $0.30, so the market puts snow at about 30%. You think it's more likely than that, so you buy 10 YES contracts at $0.30 for $3.00. What happens next:

  • It snows. The outcome settles YES, and each contract pays $1.00. You receive $10.00, a $7.00 profit.
  • It doesn't snow. The outcome settles NO, and your contracts pay $0.00. You lose the $3.00 you paid, and never more than that.
  • You sell early. A week before Christmas the forecast turns snowy, and the YES bid rises to $0.55. You sell all 10 contracts for $5.50 and lock in a $2.50 profit without waiting for the result.

If you thought it wouldn't snow, you'd buy the NO contract instead, at around $0.70. Ten NO contracts would cost about $7.00 and pay $10.00 if the day stays dry. These figures leave out commission and exchange fees, which the preflight step below shows you before you trade.

Get your account ID

The browsing endpoints only need an access token, but quotes and orders need an account ID. Retrieve it with the accounts endpoint:

REST API requestbash
curl --request GET \
  --url https://api.public.com/userapigateway/trading/account \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Use the ID of the brokerage account you want to trade from:

REST API responsejson
{
  "accounts": [
    {
      "accountId": "YOUR_ACCOUNT_ID",
      "accountType": "BROKERAGE",
      "optionsLevel": "NONE",
      "brokerageAccountType": "CASH",
      "tradePermissions": "BUY_AND_SELL"
    }
  ]
}

Set up the Python SDK

Every step below includes an HTTP example and a Python example. To follow along in Python, install version 0.1.26 or later of the Public Python SDK. That release added prediction market support:

Install the Python SDKbash
pip install "publicdotcom-py>=0.1.26"

The SDK generates access tokens for you. Create the client with the API secret key from your Public account settings, and set your account ID as the default so you don't have to pass it to every call:

Python SDKpython
from public_api_sdk import (
    ApiKeyAuthConfig,
    PublicApiClient,
    PublicApiClientConfiguration,
)

client = PublicApiClient(
    ApiKeyAuthConfig(api_secret_key="YOUR_API_SECRET_KEY"),
    config=PublicApiClientConfiguration(
        default_account_number="YOUR_ACCOUNT_ID"
    ),
)

Browse event categories

Start by listing the categories you can browse with Get event categories:

REST API requestbash
curl --request GET \
  --url https://api.public.com/userapigateway/eventcontract/summary/categories \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Each category lists its subcategories and the recurrence frequencies you can filter on (trimmed for brevity):

REST API responsejson
{
  "categories": [
    {
      "category": "Crypto",
      "subcategories": ["All", "BTC", "ETH", "SOL", "XRP"],
      "eventFrequency": {
        "show": true,
        "frequencies": [
          "ALL",
          "ONCE",
          "FIFTEEN_MINUTES",
          "ONE_HOUR",
          "ONE_DAY",
          "ONE_WEEK",
          "ONE_MONTH",
          "ONE_YEAR"
        ]
      }
    },
    {
      "category": "Economics",
      "subcategories": ["All", "Fed", "GDP", "Housing", "Inflation", "Jobs & Economy"],
      "eventFrequency": {
        "show": false,
        "frequencies": []
      }
    }
  ]
}

When eventFrequency.show is true, the category has recurring events (for example, an hourly Bitcoin price market), and you can narrow results by frequency in the next step.

The same call with the Python SDK:

Python SDKpython
response = client.get_event_categories()

for category in response.categories:
    print(f"{category.category}: {', '.join(category.subcategories)}")

Find events

Pass a category, and optionally a subcategory, to Get event summary to list its events. sortingMode is required and accepts VOLUME, EXPIRATION, or RECENTLY_ADDED:

REST API requestbash
curl --request POST \
  --url https://api.public.com/userapigateway/eventcontract/summary \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "category": "Economics",
    "subcategory": "Fed",
    "sortingMode": "VOLUME",
    "displayResolvedEvents": false
  }'

The response is a page of event summaries. Summaries don't include outcomes or prices; they give you the eventSymbol to look up next:

REST API responsejson
{
  "content": [
    {
      "eventSymbol": "KALSHI.KXRATECUTCOUNT-26DEC31",
      "title": "Number of rate cuts in 2026?",
      "volume": "20985664.14",
      "resolutionTime": "2027-01-01T04:59:00+00:00",
      "resolved": false,
      "halted": false,
      "category": "Economics",
      "subcategories": ["Fed"],
      "symbols": []
    },
    {
      "eventSymbol": "KALSHI.KXFEDDECISION-26OCT",
      "title": "Fed decision in Oct 2026?",
      "volume": "18273927.72",
      "resolutionTime": "2026-10-28T17:59:00+00:00",
      "resolved": false,
      "halted": false,
      "category": "Economics",
      "subcategories": ["Fed"],
      "symbols": []
    }
  ],
  "nextToken": "MTAw"
}

Each page holds up to 100 events. When the response includes a nextToken, send the same request with "nextToken": "MTAw" added to fetch the next page. The token is absent on the last page.

Other optional fields narrow the results further: createdWithinDays returns only recently listed events, and the filters object accepts eventIds, frequencies, and a resolutionTimeStart / resolutionTimeEnd window. When you send filters, include both eventIds and frequencies, even if one of them is an empty array.

In Python, build an EventSummaryRequest and follow next_token until it's None to collect every page:

Python SDKpython
from public_api_sdk import EventSortingMode, EventSummaryRequest

request = EventSummaryRequest(
    category="Economics",
    subcategory="Fed",
    sorting_mode=EventSortingMode.VOLUME,
    display_resolved_events=False,
)

events = []
while True:
    page = client.get_event_summary(request)
    events.extend(page.content)
    if page.next_token is None:
        break
    request.next_token = page.next_token

for event in events[:5]:
    print(f"{event.event_symbol}: {event.title} (volume {event.volume})")

Get event details

Pass an eventSymbol to Get event details to load its outcomes, the YES/NO contracts for each outcome, and their current pricing. By default every outcome is returned; set includeAllOutcomes=false to return a shorter list for events with many outcomes:

REST API requestbash
curl --request GET \
  --url 'https://api.public.com/userapigateway/eventcontract/details/KALSHI.KXFEDDECISION-26OCT?includeAllOutcomes=false' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

The response describes the full event. It's trimmed here to its first outcome; this event returns five:

REST API responsejson
{
  "eventSymbol": "KALSHI.KXFEDDECISION-26OCT",
  "exchange": "KALSHI",
  "resolved": false,
  "halted": false,
  "title": "Fed decision in Oct 2026?",
  "category": "Economics",
  "volume": "18273927.72",
  "subcategories": ["Fed"],
  "cftcContract": {
    "contractTermsUrl": "https://assets.kalshi.com/contract_terms/FEDDECISION.pdf",
    "prohibitions": [
      "Persons who are employed by any of the Source Agencies are not permitted to trade on the Contract.",
      "Persons who hold any material, non-public information on the Underlying are not permitted to trade on the Contract."
    ],
    "resolutionSources": [
      {
        "name": "Federal Reserve",
        "url": "https://www.federalreserve.gov"
      }
    ]
  },
  "outcomeCount": 5,
  "outcomes": [
    {
      "outcomeId": "KXFEDDECISION-26OCT-H0",
      "title": "Fed maintains rate",
      "rules": "If the Federal Reserve does a Hike of 0bps on October 28, 2026, then the market resolves to Yes. ...",
      "volume": "8227362.2",
      "state": "STATE_OPEN",
      "timeline": {
        "openTime": "2025-09-29T14:00:00+00:00",
        "closeTime": "2026-10-28T17:59:00+00:00",
        "expectedExpirationTime": "2026-10-28T18:05:00+00:00",
        "latestExpirationTime": "2027-01-27T18:01:00+00:00",
        "settlementTime": "2026-10-28T18:09:57+00:00",
        "settlementDelaySeconds": 297
      },
      "settledOutcome": "SETTLED_OUTCOME_UNSPECIFIED",
      "trading": "BUY_AND_SELL",
      "contracts": [
        {
          "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
          "predictedOutcome": "YES",
          "bid": "0.79",
          "ask": "0.80",
          "last": "0.80",
          "openInterest": "1555662.72",
          "dailyGainValue": "0.00",
          "dailyGainPercentage": "0.00",
          "probability": "0.80"
        },
        {
          "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.N",
          "predictedOutcome": "NO",
          "bid": "0.20",
          "ask": "0.21",
          "last": "0.20",
          "openInterest": "1555662.72",
          "dailyGainValue": "0.00",
          "dailyGainPercentage": "0.00",
          "probability": "0.20"
        }
      ]
    }
  ]
}

The fields to check before trading:

  • contracts[].symbol is the symbol you quote and trade. Use the .Y contract to bet the outcome happens and the .N contract to bet it doesn't.
  • rules states exactly what makes the outcome resolve to YES. Read it, along with the contract terms at cftcContract.contractTermsUrl, before you trade.
  • trading must be BUY_AND_SELL to open a position. LIQUIDATION_ONLY allows only closing trades, and DISABLED allows none.
  • state moves from STATE_OPEN to STATE_CLOSED at timeline.closeTime, then to STATE_SETTLED once the result is known. After settlement, settledOutcome reports SETTLED_OUTCOME_YES or SETTLED_OUTCOME_NO.
  • probabilityis derived from the YES contract's last price. For a NO contract, it is 1 minus that price.

In Python, the response is parsed into typed models, with prices as Decimalvalues and states as enums. This prints each outcome's implied probability and whether it's open for new positions:

Python SDKpython
from public_api_sdk import EventContractSide, EventTradingMode

details = client.get_event_details(
    "KALSHI.KXFEDDECISION-26OCT", include_all_outcomes=False
)

print(f"{details.title} ({details.outcome_count} outcomes)")
for outcome in details.outcomes:
    yes = next(
        contract
        for contract in outcome.contracts
        if contract.predicted_outcome == EventContractSide.YES
    )
    tradable = outcome.trading == EventTradingMode.BUY_AND_SELL
    print(
        f"  {outcome.title}: {yes.probability:.0%} "
        f"(bid {yes.bid} / ask {yes.ask}) "
        f"symbol={yes.symbol} tradable={tradable}"
    )

Get live quotes

Event details give you a pricing snapshot. For a live quote with bid and ask sizes, pass contract symbols to the quotes endpoint with type EVENTCONTRACT:

REST API requestbash
curl --request POST \
  --url https://api.public.com/userapigateway/marketdata/{YOUR_ACCOUNT_ID}/quotes \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "instruments": [
      {
        "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
        "type": "EVENTCONTRACT"
      },
      {
        "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.N",
        "type": "EVENTCONTRACT"
      }
    ]
  }'

Each quote includes the bid, ask, and their sizes in contracts:

REST API responsejson
{
  "quotes": [
    {
      "instrument": {
        "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
        "type": "EVENTCONTRACT"
      },
      "outcome": "SUCCESS",
      "last": "0.8000",
      "lastTimestamp": "2026-10-13T15:37:07.607717+00:00",
      "bid": "0.79",
      "bidSize": 265,
      "ask": "0.80",
      "askSize": 335,
      "openInterest": 1555662,
      "previousClose": "0.84",
      "oneDayChange": {
        "change": "-0.0400",
        "percentChange": "-4.76"
      }
    },
    {
      "instrument": {
        "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.N",
        "type": "EVENTCONTRACT"
      },
      "outcome": "SUCCESS",
      "last": "0.2",
      "lastTimestamp": "2026-10-13T15:37:07.607829+00:00",
      "bid": "0.20",
      "bidSize": 335,
      "ask": "0.21",
      "askSize": 265,
      "openInterest": 1555662,
      "previousClose": "0.16",
      "oneDayChange": {
        "change": "0.04",
        "percentChange": "25.00"
      }
    }
  ]
}

The same call with the Python SDK:

Python SDKpython
from public_api_sdk import InstrumentType, OrderInstrument

quotes = client.get_quotes(
    [
        OrderInstrument(
            symbol="KALSHI.KXFEDDECISION-26OCT-H0.Y",
            type=InstrumentType.EVENTCONTRACT,
        ),
        OrderInstrument(
            symbol="KALSHI.KXFEDDECISION-26OCT-H0.N",
            type=InstrumentType.EVENTCONTRACT,
        ),
    ]
)

for quote in quotes:
    print(
        f"{quote.instrument.symbol}: "
        f"bid {quote.bid} x {quote.bid_size}, ask {quote.ask} x {quote.ask_size}"
    )

Get price history

To chart how implied probabilities have moved, request bars for up to 8 contracts of one event in a single call. This endpoint uses charting identifiers, which you build from the symbols you already have:

  • The event ID is the eventSymbol plus -EVENT, for example KALSHI.KXFEDDECISION-26OCT-EVENT.
  • Each contract symbol gets -EVENTCONTRACT appended, for example KALSHI.KXFEDDECISION-26OCT-H0.Y-EVENTCONTRACT.

Pass the period (DAY, WEEK, MONTH, or ALL) in the path and a comma-separated list of contract symbols in the symbols query parameter:

REST API requestbash
curl --request GET \
  --url 'https://api.public.com/userapigateway/historicdata/event-contracts/KALSHI.KXFEDDECISION-26OCT-EVENT/bars/WEEK?symbols=KALSHI.KXFEDDECISION-26OCT-H0.Y-EVENTCONTRACT' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

The response holds one chart per contract (bars trimmed for brevity):

REST API responsejson
{
  "period": "WEEK",
  "charts": [
    {
      "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y-EVENTCONTRACT",
      "previousClosePrice": "0.8100",
      "currentPrice": "0.8000",
      "totalGainLoss": "-0.0100",
      "totalGainLossPercentage": "-1.23",
      "bars": [
        {
          "timestamp": "2026-10-06T11:37:07-04:00",
          "open": "0.8100",
          "close": "0.8300",
          "high": "0.8300",
          "low": "0.8100",
          "value": "0.8300",
          "volume": "1095",
          "gainAmount": "0.0200",
          "gainPercentage": "2.47"
        },
        {
          "timestamp": "2026-10-06T12:37:07-04:00",
          "open": "0.8300",
          "close": "0.8300",
          "high": "0.8300",
          "low": "0.8200",
          "value": "0.8300",
          "volume": "299",
          "gainAmount": "0.0200",
          "gainPercentage": "2.47"
        }
      ]
    }
  ]
}

Bar prices are on the same 0.00 to 1.00 scale as quotes, so you can read them directly as implied probabilities. A .N symbol returns NO prices. Charts in one response can start at different times, so align them by timestamp rather than by position. A symbol with no prices in the period is left out of charts.

The same call with the Python SDK:

Python SDKpython
from public_api_sdk import EventContractBarPeriod

event_symbol = "KALSHI.KXFEDDECISION-26OCT"
contract_symbols = [
    "KALSHI.KXFEDDECISION-26OCT-H0.Y",
    "KALSHI.KXFEDDECISION-26OCT-H25.Y",
]

response = client.get_event_contract_bars(
    event_id=f"{event_symbol}-EVENT",
    period=EventContractBarPeriod.WEEK,
    symbols=[f"{symbol}-EVENTCONTRACT" for symbol in contract_symbols],
)

for chart in response.charts:
    print(
        f"{chart.symbol}: {len(chart.bars)} bars, "
        f"now {chart.current_price} ({chart.total_gain_loss_percentage}% this week)"
    )

Run preflight check

Once you've picked a contract, run a preflight check to see the estimated cost and fees before you commit. This example buys 10 YES contracts on "Fed maintains rate" with a limit price of $0.80. quantity is the number of contracts:

REST API requestbash
curl --request POST \
  --url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/preflight/single-leg \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "instrument": {
      "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
      "type": "EVENTCONTRACT"
    },
    "orderSide": "BUY",
    "orderType": "LIMIT",
    "limitPrice": "0.80",
    "expiration": {
      "timeInForce": "DAY"
    },
    "quantity": "10"
  }'

The response shows the order value plus commission and exchange fees:

REST API responsejson
{
  "instrument": {
    "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
    "type": "EVENTCONTRACT"
  },
  "estimatedCommission": "0.1000000",
  "regulatoryFees": {
    "secFee": "0.00",
    "tafFee": "0.00",
    "exchangeFee": "0.1000000"
  },
  "orderValue": "8.0000000",
  "estimatedQuantity": "10.00000",
  "estimatedCost": "8.20",
  "buyingPowerRequirement": "8.2000000",
  "marginRequirement": {
    "longMaintenanceRequirement": "1.0000",
    "longInitialRequirement": "1.0000"
  },
  "marginImpact": {
    "marginUsageImpact": "8.20",
    "initialMarginRequirement": "8.20"
  }
}

Ten contracts at $0.80 is an $8.00 order value, and the fees bring the estimated cost to $8.20. The margin requirement of 1.0000 means event contracts are fully paid: the full cost comes out of your buying power.

The same preflight with the Python SDK:

Python SDKpython
from decimal import Decimal

from public_api_sdk import (
    InstrumentType,
    OrderExpirationRequest,
    OrderInstrument,
    OrderSide,
    OrderType,
    PreflightRequest,
    TimeInForce,
)

contract = OrderInstrument(
    symbol="KALSHI.KXFEDDECISION-26OCT-H0.Y",
    type=InstrumentType.EVENTCONTRACT,
)

preflight = client.perform_preflight_calculation(
    PreflightRequest(
        instrument=contract,
        order_side=OrderSide.BUY,
        order_type=OrderType.LIMIT,
        limit_price=Decimal("0.80"),
        expiration=OrderExpirationRequest(time_in_force=TimeInForce.DAY),
        quantity=Decimal("10"),
    )
)

print(f"Order value: ${preflight.order_value:.2f}")
print(f"Commission: ${preflight.estimated_commission:.2f}")
print(f"Estimated cost: ${preflight.estimated_cost:.2f}")

Place the order

Place the order with the same fields plus a unique UUID for the orderId:

REST API requestbash
curl --request POST \
  --url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/order \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "orderId": "5f0c2a7e-3b9d-4e61-9a0f-2c8d7b14e6a3",
    "instrument": {
      "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
      "type": "EVENTCONTRACT"
    },
    "orderSide": "BUY",
    "orderType": "LIMIT",
    "limitPrice": "0.80",
    "expiration": {
      "timeInForce": "DAY"
    },
    "quantity": "10"
  }'

The response confirms the order was submitted:

REST API responsejson
{
  "orderId": "5f0c2a7e-3b9d-4e61-9a0f-2c8d7b14e6a3"
}

Order placement is asynchronous. A successful response means the order was received, not that it was filled.

In Python, reuse the contract from the preflight step. The SDK returns a NewOrder handle you can use to track the order:

Python SDKpython
import uuid

from public_api_sdk import OrderRequest

new_order = client.place_order(
    OrderRequest(
        order_id=str(uuid.uuid4()),
        instrument=contract,
        order_side=OrderSide.BUY,
        order_type=OrderType.LIMIT,
        limit_price=Decimal("0.80"),
        expiration=OrderExpirationRequest(time_in_force=TimeInForce.DAY),
        quantity=Decimal("10"),
    )
)

print(f"Order submitted: {new_order.order_id}")

Check order status

Poll the order to see whether it has filled:

REST API requestbash
curl --request GET \
  --url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/order/5f0c2a7e-3b9d-4e61-9a0f-2c8d7b14e6a3 \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

The response shows the current status and fill details:

REST API responsejson
{
  "orderId": "5f0c2a7e-3b9d-4e61-9a0f-2c8d7b14e6a3",
  "instrument": {
    "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
    "type": "EVENTCONTRACT"
  },
  "createdAt": "2026-10-13T15:40:12.481920Z",
  "type": "LIMIT",
  "side": "BUY",
  "status": "FILLED",
  "quantity": "10",
  "notionalValue": null,
  "expiration": {
    "timeInForce": "DAY",
    "expirationTime": null
  },
  "limitPrice": "0.80",
  "stopPrice": null,
  "closedAt": "2026-10-13T15:40:13.027514Z",
  "openCloseIndicator": "OPEN",
  "filledQuantity": "10",
  "averagePrice": "0.80",
  "legs": null,
  "rejectReason": null
}

In Python, wait_for_status polls the order for you until it reaches one of the statuses you pass, or the timeout runs out:

Python SDKpython
from public_api_sdk import OrderStatus

order = new_order.wait_for_status(
    [
        OrderStatus.FILLED,
        OrderStatus.CANCELLED,
        OrderStatus.REJECTED,
        OrderStatus.EXPIRED,
    ],
    timeout=60,
)

print(
    f"{order.status.value}: {order.filled_quantity} filled "
    f"at an average of {order.average_price}"
)

Verify your position

Check your portfolio to confirm the new event contract position:

REST API requestbash
curl --request GET \
  --url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/portfolio/v2 \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

The position appears alongside your other holdings with type EVENTCONTRACT (other positions omitted):

REST API responsejson
{
  "accountId": "YOUR_ACCOUNT_ID",
  "accountType": "BROKERAGE",
  "buyingPower": {
    "cashOnlyBuyingPower": "991.80",
    "buyingPower": "991.80",
    "optionsBuyingPower": "991.80"
  },
  "positions": [
    {
      "instrument": {
        "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
        "name": "Fed decision in Oct 2026? - Fed maintains rate - Yes",
        "type": "EVENTCONTRACT"
      },
      "quantity": "10",
      "currentValue": "8.00",
      "percentOfPortfolio": "0.80",
      "costBasis": {
        "totalCost": "8.20",
        "unitCost": "0.82",
        "gainValue": "-0.20",
        "gainPercentage": "-2.44"
      }
    }
  ]
}

In Python, filter the positions by instrument type to list only your event contracts:

Python SDKpython
portfolio = client.get_portfolio()

for position in portfolio.positions:
    if position.instrument.type == InstrumentType.EVENTCONTRACT:
        print(
            f"{position.instrument.symbol}: {position.quantity} contracts, "
            f"current value ${position.current_value}"
        )

Exit the position or hold to settlement

You have two ways out of a position:

  • Sell before the market closes to lock in a gain or cut a loss at the current price. Place a SELLorder for the same contract symbol, as shown below. This works while the outcome's trading field is BUY_AND_SELL or LIQUIDATION_ONLY.
  • Hold to settlement. After the outcome is determined, each winning contract pays $1.00 and each losing contract pays $0.00. Payouts arrive after timeline.settlementTime, which already includes the settlementDelaySeconds buffer.
REST API requestbash
curl --request POST \
  --url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/order \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "orderId": "9b3e1d42-7c5a-4f08-b6d2-e1a4c9f07d58",
    "instrument": {
      "symbol": "KALSHI.KXFEDDECISION-26OCT-H0.Y",
      "type": "EVENTCONTRACT"
    },
    "orderSide": "SELL",
    "orderType": "LIMIT",
    "limitPrice": "0.85",
    "expiration": {
      "timeInForce": "DAY"
    },
    "quantity": "10"
  }'

The same sell order with the Python SDK:

Python SDKpython
sell_order = client.place_order(
    OrderRequest(
        order_id=str(uuid.uuid4()),
        instrument=contract,
        order_side=OrderSide.SELL,
        order_type=OrderType.LIMIT,
        limit_price=Decimal("0.85"),
        expiration=OrderExpirationRequest(time_in_force=TimeInForce.DAY),
        quantity=Decimal("10"),
    )
)

print(f"Sell order submitted: {sell_order.order_id}")

📝 Important Notes

  • Every event contract settles at $1.00 or $0.00. Your maximum loss on a purchase is what you paid, and your maximum profit is $1.00 per contract minus that cost.
  • Discovery uses the eventSymbol (for example, KALSHI.KXFEDDECISION-26OCT), while quotes and orders use a contract symbol ending in .Y or .N. Price history adds -EVENT and -EVENTCONTRACT suffixes to those symbols.
  • Use LIMIT orders. Many outcomes have thin order books, and a limit price protects you from filling far from the quote.
  • Check halted on the event and trading on the outcome before you place an order. Halted or disabled markets reject new orders.
  • Review the prohibitions in cftcContract. Some people, such as employees of the resolution source, aren't permitted to trade certain contracts.
  • Always run preflight checks to understand costs before placing orders.