> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rivermarkets.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> Official Python SDK for the River Markets API

## Installation

<CodeGroup>
  ```bash pip theme={null}
  pip install rivermarkets
  ```

  ```bash uv theme={null}
  uv add rivermarkets
  ```
</CodeGroup>

## Quick Start

```python theme={null}
from datetime import datetime
from rivermarkets import RiverMarkets

client = RiverMarkets(
    key_id="<uuid from Settings → API Keys>",
    private_key="<base64 private key shown once at creation>",
)

# Search for shutdown related markets on Kalshi.
# Filter to active markets that haven't expired yet so the results are tradeable —
# apply both `status=active` and `expiration_date_start` (now) for any exchange.
results = client.markets.search_markets(
    q="shutdown",
    exchange_name="KALSHI",
    expiration_date_start=datetime.now(),
    status="active",
)
for market in results.results:
    print(f"River ID: {market.river_id}, Ticker: {market.ticker}")
```

See [Market Data → Search Markets](#search-markets) for the full list of filters.

The SDK signs every REST request and WebSocket handshake locally with Ed25519.
Your private key never leaves the process. See
[Authentication](/api-reference/authentication) for the canonical-string spec
if you need to verify what the SDK is producing.

## Async Support

```python theme={null}
from rivermarkets import AsyncRiverMarkets

client = AsyncRiverMarkets(key_id="<uuid>", private_key="<base64-priv>")
results = await client.markets.search_markets(
    q="shutdown",
    exchange_name="KALSHI",
    expiration_date_start=datetime.now(),
    status="active",
)
```

## Trading

### Place an Order

```python theme={null}
order = client.orders.create_order(
    subaccount_id="your_subaccount_id",
    river_id=4552150,
    order_type="LIMIT",
    time_in_force="GTC",
    buy_flag=True,
    price=0.50,
    qty=10,
)
print(f"Order placed: {order.river_order_id}")
```

### List Orders

```python theme={null}
orders = client.orders.list_orders(status="RESTING")
for order in orders.orders:
    print(f"{order.river_order_id}: {order.status}")
```

### Cancel an Order

```python theme={null}
client.orders.cancel_order(order_id="your_order_id")
```

### Complex Orders

Complex orders let you place iceberg slices, pegged orders, smart-taker orders, and
take-profit / stop-loss triggers. Pass exactly one of `iceberg_order_params`,
`peg_order_params`, `smart_taker_order_params`, or `conditional_order_params` along
with `subaccount_id` and either `river_id` or `generic_asset_id`. The endpoint returns
202 with the order in `PENDING` status — it's activated asynchronously.

#### Iceberg

```python theme={null}
from rivermarkets import RiverMarkets
from rivermarkets.types import IcebergOrderParams

client = RiverMarkets(key_id="...", private_key="...")

order = client.complex_orders.create_complex_order(
    subaccount_id="sub_abc",
    river_id=4552150,
    iceberg_order_params=IcebergOrderParams(
        buy_flag=True,
        total_qty=1000,
        displayed_qty=50,
        limit_price=0.42,
        post_only=True,
    ),
)
```

#### Peg

```python theme={null}
from rivermarkets.types import PegOrderParams

order = client.complex_orders.create_complex_order(
    subaccount_id="sub_abc",
    river_id=4552150,
    peg_order_params=PegOrderParams(
        buy_flag=True,
        total_qty=100,
        max_price=0.55,        # ceiling — a buy peg never pays more than this
        post_only=True,
        max_qty_level=5000,    # skip levels bigger than this; rest one tick inside instead
    ),
)
```

#### Smart Taker

```python theme={null}
from rivermarkets.types import SmartTakerOrderParams

order = client.complex_orders.create_complex_order(
    subaccount_id="sub_abc",
    river_id=4552150,
    smart_taker_order_params=SmartTakerOrderParams(
        buy_flag=True,
        total_qty=500,
        limit_price=0.30,    # worst price the taker will cross at
        min_qty=100,         # only fire when this much liquidity is available within limit_price
    ),
)
```

#### Take-profit attached to a parent order

```python theme={null}
from rivermarkets.types import ConditionalOrderCreate, TriggerOrder

parent = client.orders.create_order(
    subaccount_id="sub_abc", river_id=4552150,
    order_type="LIMIT", time_in_force="GTC",
    buy_flag=True, price=0.40, qty=100,
)

tp = client.complex_orders.create_complex_order(
    subaccount_id="sub_abc",
    river_id=4552150,
    conditional_order_params=ConditionalOrderCreate(
        conditional_order_type="TP",
        parent_river_order_id=parent.river_order_id,
        trigger_order=TriggerOrder(
            order_type="LIMIT", buy_flag=False, qty=100, price=0.55,
        ),
    ),
)
```

#### Standalone stop

```python theme={null}
stop = client.complex_orders.create_complex_order(
    subaccount_id="sub_abc",
    river_id=4552150,
    conditional_order_params=ConditionalOrderCreate(
        conditional_order_type="STOP",
        stop_order_price=0.30,
        trigger_order=TriggerOrder(
            order_type="MARKET", buy_flag=False, qty=100,
        ),
    ),
)
```

<Note>
  * Prices and `limit_price` are 0–1 (Kalshi-style probability), not dollars.
  * For TP/SL you must supply `parent_river_order_id` (or `parent_complex_order_id` to
    chain). For standalone STOP, omit parents and set `stop_order_price`.
  * `TriggerOrder.price` is required when `order_type="LIMIT"`, omit for `"MARKET"`.
</Note>

## Portfolio

### Get Positions

```python theme={null}
positions = client.positions.get_positions()
```

### Get Fills

```python theme={null}
fills = client.fills.get_fills(subaccount_id="your_subaccount_id")
```

## Market Data

### Search Markets

<Note>
  When using a free-text `q=`, always pair it with date filters
  (`expiration_date_start`, `start_datetime_after`/`before`) and `status="active"` —
  otherwise results include long-expired markets and the ranking is dominated by
  historical noise. See the Quick Start above for the recommended shape.
</Note>

```python theme={null}
# Filter to one event
r = client.markets.search_markets(event_ticker="KXNBA-26", limit=20)

# Filter to multiple categories (note: category accepts a list)
r = client.markets.search_markets(
    category=["Sports", "Crypto"],
    subcategory="Basketball",
    sort_by="volume",
    limit=50,
)

# Paginate by events instead of markets
r = client.markets.search_markets(
    event_limit=10,
    event_offset=0,
)
```

#### Parameters

<ParamField query="q" type="string">
  Search query.
</ParamField>

<ParamField query="exchange_name" type="string">
  Filter by exchange name (`KALSHI`, `POLYMARKET`).
</ParamField>

<ParamField query="category" type="list[string]">
  Filter by canonical category. Pass a list to filter to multiple: `Sports`,
  `Crypto`, `Politics`, `Finance`, `Entertainment`, `Science & Tech`, `Weather`,
  `World Affairs`, `Health`, `Social`, `Other`.
</ParamField>

<ParamField query="subcategory" type="string">
  Filter by subcategory (e.g. `Basketball`, `Football`).
</ParamField>

<ParamField query="status" type="InstrumentStatus">
  Filter by instrument status. Defaults to active markets.
</ParamField>

<ParamField query="expiration_date_start" type="string (ISO 8601)">
  Start of expiration date range (inclusive).
</ParamField>

<ParamField query="expiration_date_end" type="string (ISO 8601)">
  End of expiration date range (exclusive).
</ParamField>

<ParamField query="start_datetime_after" type="string (ISO 8601)">
  Filter to markets with `start_datetime >=` this.
</ParamField>

<ParamField query="start_datetime_before" type="string (ISO 8601)">
  Filter to markets with `start_datetime <` this.
</ParamField>

<ParamField query="event_ticker" type="string">
  Filter by `event_ticker` (exact match).
</ParamField>

<ParamField query="sort_by" type="string">
  Sort mode for event-based pagination: `trending`, `volume`, `newest`,
  `ending-soon`, `start-time`.
</ParamField>

<ParamField query="limit" type="integer" default="20">
  Maximum number of results (1–1000).
</ParamField>

<ParamField query="offset" type="integer" default="0">
  Offset for pagination.
</ParamField>

<ParamField query="event_limit" type="integer">
  Paginate by events instead of markets. Up to 200 by default; values above that
  require `start_datetime_after` or `start_datetime_before`.
</ParamField>

<ParamField query="event_offset" type="integer">
  Event offset for event-based pagination.
</ParamField>

### Orderbook

```python theme={null}
orderbook = client.orderbooks.get_orderbook(river_id=4552150)
```

### Price History

```python theme={null}
prices = client.prices.get_prices(river_id=4552150, type="candlesticks", interval="1d")
```

## Realtime (WebSockets)

The async client exposes three live data feeds at `client.realtime`. Each returns a
`Subscription` you use as an async context manager and async iterator. Disconnects are
handled transparently — the client reconnects and re-sends the active subscription set,
so iteration just resumes.

The underlying wire protocol is documented under
[WS API Reference](/ws-api-reference/authentication).

### Orderbook stream

```python theme={null}
import asyncio
from rivermarkets import AsyncRiverMarkets

client = AsyncRiverMarkets(
    key_id="YOUR_KEY_ID",
    private_key="YOUR_BASE64_PRIVATE_KEY",
)

async def stream_orderbook():
    async with client.realtime.orderbooks([6003721, 8927]) as stream:
        async for msg in stream:
            if msg.type == "snapshot":
                print("snapshot", msg.river_id, msg.data["best_bid_price"])
            elif msg.type == "update":
                print("update",   msg.river_id, msg.data["best_bid_price"])

asyncio.run(stream_orderbook())
```

You can mutate the subscription on the fly:

```python theme={null}
await stream.subscribe([12345, 67890])
await stream.unsubscribe([6003721])
```

### Order status stream

Order updates are pinned to one subaccount at handshake — there is no
subscribe/unsubscribe frame.

```python theme={null}
async with client.realtime.orders(subaccount_id="sub_abc") as stream:
    async for msg in stream:
        if msg.type == "snapshot":
            print(f"{len(msg.orders)} open orders at handshake")
        elif msg.type == "order":
            o = msg.model_extra
            print(f"order {o['id']} → {o['status']}")
```

### Trade tape

```python theme={null}
async with client.realtime.tradeprints([6003721]) as stream:
    async for msg in stream:
        if msg.type == "trade":
            d = msg.data
            side = "BUY" if d["aggressor_buy_flag"] else "SELL"
            print(f"{d['exchange_timestamp']} {side} {d['qty']} @ {d['price']}")
```

### Notes

* All three subscriptions reconnect transparently on disconnect; the iterator simply
  pauses and resumes.
* In Jupyter, drop `asyncio.run(...)` and use top-level `await` directly in a cell.
* `Subscription` works as a context manager — exiting the `async with` block closes the
  socket cleanly.
* `Message.type` is always set; endpoint-specific fields (`data`, `river_id`, `orders`,
  `code`, `message`) are accessed as attributes or via `msg.model_extra`.

## Subaccounts

```python theme={null}
# List subaccounts
subaccounts = client.subaccounts.list_subaccounts()

# Create a subaccount
subaccount = client.subaccounts.create_subaccount(name="My Strategy")
print(subaccount.subaccount_id)
```

## Error Handling

The SDK raises typed exceptions for API errors:

```python theme={null}
from rivermarkets.errors import UnprocessableEntityError
from rivermarkets.core.api_error import ApiError

try:
    order = client.orders.get_order(order_id="invalid")
except UnprocessableEntityError as e:
    print(f"Validation error: {e.body}")
except ApiError as e:
    print(f"API error {e.status_code}: {e.body}")
```

## Configuration

```python theme={null}
client = RiverMarkets(
    key_id="<uuid>",
    private_key="<base64-priv>",
    base_url="https://api.rivermarkets.com",  # default
    timeout=30.0,                              # seconds, default 60
)
```

<Card title="GitHub" icon="github" href="https://github.com/rivermarkets/rivermarkets-python-sdk">
  View the source code and contribute on GitHub.
</Card>
