Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Polyester Python SDK

Official Python SDK for Polyester APIs, built for trading bots, backend jobs, research notebooks, and automation.

Status: Alpha. Proprietary license (not open source). API-key only — no browser login or JWT flows.

Requires Python 3.11+.

Install

PyPI: https://pypi.org/project/polyester-sdk/

pip install "polyester-sdk[realtime]"

The [realtime] extra installs websockets for live market data and private streams. Omit it if you only need REST/Connect RPC calls.

For development from a git checkout:

git clone https://github.com/Fabric-Labs/polyester-sdk-python.git
cd polyester-sdk-python
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,realtime]"

Quickstart

Create an API key in the Polyester app (API in the sidebar). Copy the key id and private key when shown — the private key is only displayed once.

import asyncio

from polyester import AsyncPolyester

async def main() -> None:
    async with AsyncPolyester(
        api_key_id="ak_...",           # from API key creation
        api_private_key="...",       # 64-char hex secret from API key creation
        default_account_id="...",    # Profile → Account ID (see below)
    ) as client:
        overview = await client.market_overview.list(limit=5)
        for market in overview.markets:
            print(market.symbol, market.last_price_ticks)

        open_orders = await client.orders.list_open()
        print(f"{len(open_orders.orders)} open orders")

asyncio.run(main())

Credentials

Value Where to find it Constructor parameter
API key id API → create or view key api_key_id
API private key Shown once when the key is created api_private_key
Account ID ProfileAccount ID (e.g. RLxqJGUDg92) default_account_id

Pass all credentials as constructor parameters. The SDK does not read environment variables unless you pass them in yourself (or use from_env() in scripts — see below).

api_private_key accepts the 64-character hex Ed25519 secret from key creation, or raw 32-byte key material.

default_account_id is the Account ID string from your Profile page. Use the value exactly as shown in the app. Do not use an internal numeric id.

default_account_id is optional for public market-data calls. It is required for account-scoped operations such as private realtime channels, bucket transfers, and some ledger writes.

Authentication patterns

Recommended — explicit parameters:

from polyester import AsyncPolyester

client = AsyncPolyester(
    api_key_id="ak_...",
    api_private_key="...",
    default_account_id="RLxqJGUDg92",
)

If your deployment stores secrets in environment variables, read them in your application and pass them to the constructor:

import os

from polyester import AsyncPolyester

client = AsyncPolyester(
    api_key_id=os.environ["POLYESTER_API_KEY_ID"],
    api_private_key=os.environ["POLYESTER_API_PRIVATE_KEY"],
    default_account_id=os.environ["POLYESTER_ACCOUNT_ID"],
)

The plain AsyncPolyester(...) / Polyester(...) constructors never implicitly read os.environ.

Scripts and local tests onlyAsyncPolyester.from_env() and Polyester.from_env() load POLYESTER_API_KEY_ID, POLYESTER_API_PRIVATE_KEY, and POLYESTER_ACCOUNT_ID from the process environment. This is a convenience helper, not the primary integration pattern.

Create and cancel orders

from polyester import AsyncPolyester

async with AsyncPolyester(
    api_key_id="ak_...",
    api_private_key="...",
    default_account_id="RLxqJGUDg92",
    default_sub_account_id="",  # main account; omit subaccount scoping
) as client:
    result = await client.orders.create(
        symbol="BNB-USDT",
        side="buy",
        order_type="limit",
        tif="gtc",
        qty="0.01",
        price="100",
        post_only=True,
        client_order_id="my-bot-001",
    )
    print(result.status, result.order_id)

    await client.orders.cancel(client_order_id="my-bot-001")

Use decimal strings for qty and price. Do not pass floats.

Your API key needs a policy that allows trading. Spot orders spend trading balance (see below).

Balances: funding vs trading

Ledger balances have separate funding and trading buckets per asset.

  • Deposits land in funding.
  • Spot orders spend trading balance.
  • Move funds funding → trading in the Polyester UI (Funding → Unified Trading) or on-chain via the funding wallet.

SDK notes:

  • Funding → trading: on-chain TradingGateway.deposit (not an API-key RPC).
  • Funding → another user's funding wallet: on-chain FundingAccount.UAssetTransfer via wallet/smart-account signing in the Polyester app (not an API-key RPC). ledger_write.create_funding_user_transfer is internal-only and is not mounted on the public API host.
  • Trading → funding: client.trading_withdraws.create_to_funding(...) with a signed intent payload.
  • Trading → trading (another account): client.internal_transfers.create(...) or client.ledger_write.transfer_trading_to_trading(...).

Pass default_account_id (your Profile Account ID) on the client for bucket transfers and other account-scoped ledger operations.

Format u128 wire amounts with the public helper (18-decimal scale):

from polyester import format_ledger_u128

print(format_ledger_u128(balance.funding), format_ledger_u128(balance.trading))

Public market data

Public endpoints do not require an API key. Authenticated endpoints use the credentials above.

candles = await client.market_data.get_candles(symbol="BTC-USDT", timeframe="1m", limit=50)
current = await client.market_data.get_current_candle(symbol="BTC-USDT", timeframe="1m")
trades = await client.market_data.get_trades(symbol="BTC-USDT", limit=20)

subscription = await client.market_data.subscribe_trades(symbol="BNB-USDT")
try:
    async for trade in subscription:
        print(trade.price_ticks, trade.qty_scaled)
        break
finally:
    await subscription.aclose()

Merged market overview stream (snapshot + live updates):

sub = await client.market_overview.create_subscription()
async for markets in sub:
    print(len(markets), "rows")
    break
await sub.aclose()

Sync client

The sync Polyester client exposes the same service tree and constructor parameters:

from polyester import Polyester

with Polyester(
    api_key_id="ak_...",
    api_private_key="...",
    default_account_id="RLxqJGUDg92",
) as client:
    balances = client.balances.list()

Realtime subscriptions are available via subscribe_sync helpers on the sync client.

Testing (contributors)

CI (no network): python -m pytest tests/unit -q

Live devnet tests use a local .env file in the test harness only. Fixtures load values from env and pass them as explicit constructor parameters — the same pattern application code should use.

cp .env.example .env
# fill in POLYESTER_API_KEY_ID, POLYESTER_API_PRIVATE_KEY, POLYESTER_ACCOUNT_ID

pip install -e ".[dev,realtime]"
python -m pytest tests/unit -q
./scripts/test_all.sh   # optional: unit + live tiers
./scripts/smoke_realtime.sh   # realtime unit + live heartbeat before release

Use python -m pytest (not bare pytest) so tests run in the same venv as pip install.

Pre-release checklist (realtime changes):

cd polyester-sdk-python
python -m venv /tmp/polyester-pypi-test && source /tmp/polyester-pypi-test/bin/activate
pip install -e ".[dev,realtime]"
python -m pytest tests/unit -q
./scripts/smoke_realtime.sh

cd ../polyester-examples-python
pip install -e "../polyester-sdk-python[realtime]"
python -m pytest -q
python3 examples/04_public_realtime_trades.py
python3 examples/05_public_orderbook_stream.py

Then bump the version, update CHANGELOG.md, build, and publish to PyPI. Install from the new wheel (not editable) and rerun smoke_realtime.sh once to confirm the published artifact.

Changelog

See CHANGELOG.md.

Transport

Connect RPC over HTTP via generated clients in src/polyester/gen/. Wire format defaults to binary protobuf; pass wire_format="json" for debugging.

Some RPCs may return HTTP 404 on devnet. The SDK raises PolyesterRouteNotFoundError with a clearer message than [unimplemented]: Not Found.

Download files

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

Source Distribution

polyester_sdk-0.1.0a5.tar.gz (288.3 kB view details)

Uploaded Source

Built Distribution

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

polyester_sdk-0.1.0a5-py3-none-any.whl (399.4 kB view details)

Uploaded Python 3

File details

Details for the file polyester_sdk-0.1.0a5.tar.gz.

File metadata

  • Download URL: polyester_sdk-0.1.0a5.tar.gz
  • Upload date:
  • Size: 288.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for polyester_sdk-0.1.0a5.tar.gz
Algorithm Hash digest
SHA256 02dd1103b42fefc3d4edb6c2ef41e6473793bda8c1053bfa8db5c79092c4b9a0
MD5 ae6f00a0c1902fd924890c404c981bf4
BLAKE2b-256 32adf51758fd635dfe364255048ddd4a4c7c3a966e73032ffc83f0f32fac5159

See more details on using hashes here.

File details

Details for the file polyester_sdk-0.1.0a5-py3-none-any.whl.

File metadata

File hashes

Hashes for polyester_sdk-0.1.0a5-py3-none-any.whl
Algorithm Hash digest
SHA256 6d8500472babdffeb09b41d8da8d4a739c9cb671617df5f18b6dc7a17421ea5b
MD5 1f696e5b8a630536726201a36d6977aa
BLAKE2b-256 f2aec5bf472dc8553331e17d4857eb81ee53ea5d4f244da077f4ab05f4758617

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0a5 This release

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