Skip to main content

parlayx

Official Python client for the ParlayX public API, a single trading interface over aggregated prediction markets.

Documentation

Full guides, authentication, and API reference live at docs.parlayx.com.

Install

pip install parlayx

Requires Python 3.11 or newer. Requests are signed with your Ed25519 key, so the client runs server-side: the private key stays in your own process and only the key id, a timestamp, and the signature go on the wire.

Usage

Set your credentials in the environment:

export PARLAYX_KEY_ID="3f7c1b2a-9d4e-4a61-b8f0-2c5d7e91a4b3"
export PARLAYX_PRIVATE_KEY_HEX="a3f1...9c20"  # 64 hex characters
from parlayx import ParlayX

client = ParlayX()

who = client.whoami()
competitions = client.list_competitions(sport="spt_baseball")
balance = client.kalshi.get_balance()

PARLAYX_PRIVATE_KEY_HEX is your key's 32-byte seed as 64 lowercase hex characters.

Pass either credential directly to take it from somewhere else, such as a secret manager (vault below is your own client). An argument always wins over the environment, and each resolves on its own, so the key id can be inline while the private key stays out of your source:

client = ParlayX(private_key_hex=vault.read("parlayx/private-key"))

A credential that is neither passed nor exported raises ValueError naming the variable it wanted, at construction rather than on your first request. Only an omitted argument falls back to the environment: an argument you did pass is used as given, and a blank one is rejected rather than replaced, so a lookup of your own that returned "" cannot end up signing as whatever account the environment happens to hold.

Orders, positions and balances are venue-scoped, because the venues address markets differently:

from parlayx import KalshiOrderRequest

order = client.kalshi.submit_order(
    KalshiOrderRequest(
        ticker="KXMLBGAME-26SEP01-NYY",
        side="YES",
        action="BUY",
        count=10,
        price_cents=45,
    )
)
print(order.order_id, order.status)

Fields accept either spelling: price_cents or priceCents. The wire always carries the API's own camelCase.

Each of client.polymarket and client.kalshi carries the same seven methods: submit_order, list_orders, get_order, cancel_order, get_order_fills, list_positions and get_balance.

Idempotency

submit_order attaches a fresh idempotency key per call, so retrying a call that failed submits a second order. Pass your own key to make a specific retry safe:

client.kalshi.submit_order(order, idempotency_key="my-retry-key")

Errors

Every non-2xx response raises RequestError, carrying a stable code, the HTTP status, a message and the parsed body:

from parlayx import ApiErrorCode, RequestError

try:
    client.polymarket.get_balance()
except RequestError as error:
    if error.code == ApiErrorCode.insufficient_balance:
        ...

Pagination

Order listings are cursored. The paginators follow nextPageToken to exhaustion:

from parlayx import paginate_kalshi_orders

for order in paginate_kalshi_orders(client, status="OPEN"):
    print(order.order_id, order.status)

Positions and fills are not cursored and return their full result in one call.

Async

AsyncParlayX is the same surface with await, and apaginate_kalshi_orders and apaginate_polymarket_orders are the async paginators:

from parlayx import AsyncParlayX

async with AsyncParlayX() as client:
    who = await client.whoami()

Market data stream

The stream is a separate import, with nothing extra to install:

from parlayx.stream import StreamClient

It authenticates with the same signing key, re-signing the handshake on every connect, and replays your subscriptions across reconnects so you subscribe once:

from parlayx.stream import StreamClient

stream = StreamClient()

async with stream:
    await stream.subscribe([{"venue": "polymarket", "tokenId": "97840505..."}])
    async for frame in stream:
        if frame.type == "snapshot":
            print(frame.seq, frame.bids[:3], frame.asks[:3])
        elif frame.type == "delta":
            for change in frame.changes:
                ...  # size 0 removes the level, anything else sets it

channels defaults to ["book"]; pass ["book", "trade"] for the trade tape as well.

Connection lifecycle is reported through callbacks rather than frames, because it describes the connection rather than the market:

StreamClient(
    on_reconnecting=lambda event: log.info("reconnecting, attempt %s", event.attempt),
    on_terminated=lambda event: log.warning("stream stopped: %s", event.reason),
    on_unparseable=lambda raw: log.warning("dropped an unrecognised frame: %s", raw),
)

A terminated stream does not reconnect; build a new client to resume.

One inconsistency worth knowing

Discovery listings report venue in uppercase (KALSHI, POLYMARKET), while the order and streaming surfaces take it lowercase. The client passes the value through unchanged in both directions rather than quietly rewriting it.

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

parlayx-1.0.0.tar.gz (28.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

parlayx-1.0.0-py3-none-any.whl (35.0 kB view details)

Uploaded Python 3

File details

Details for the file parlayx-1.0.0.tar.gz.

File metadata

  • Download URL: parlayx-1.0.0.tar.gz
  • Upload date:
  • Size: 28.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for parlayx-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1d68963d449e53375a76319601903375de3454341bf9da4d52891ea6bc895fed
MD5 d16d79b02243370a245a1c3cb7d84895
BLAKE2b-256 e11eec50c4a74c7e4ed96eb7e4767dbbf7ea71fc06ad61ae807d4d44ec1cab5b

See more details on using hashes here.

Provenance

The following attestation bundles were made for parlayx-1.0.0.tar.gz:

Publisher: release-python-sdk.yml on parlayx/aggregator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file parlayx-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: parlayx-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 35.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for parlayx-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 23b609973d60cf259c92b102740438ba65b0861fd3e9dab0c1bb755b9fe988ec
MD5 ddb7724e601470758051a893caa442d3
BLAKE2b-256 d8c57699bc63cbf9d7e92ec113fa5547b273b21d579325c0b27f67a859167772

See more details on using hashes here.

Provenance

The following attestation bundles were made for parlayx-1.0.0-py3-none-any.whl:

Publisher: release-python-sdk.yml on parlayx/aggregator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.1.0

2 files

This release

1.0.0 This release

2 files

0.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page