Python SDK

drazill — sync and async clients with retries, idempotency, cursor iteration, and a WebSocket client.

The drazill package is generated from the contract and MIT-licensed. Requires Python 3.9+.

Install

$pip install drazill
$# with WebSocket support:
$pip install "drazill[websocket]"

An unconfigured client talks to staging (https://staging.drazill.com/api/v1), never production — pass base_url= or set DRAZILL_BASE_URL to target another environment.

Quickstart

Point the client at an environment with DRAZILL_BASE_URL (defaults to staging). List open markets:

quickstart_markets.py
1client = DrazillClient(api_key=API_KEY, base_url=BASE_URL)
2print(f"→ Drazill API @ {BASE_URL}\n")
3
4# 1) List a page of open markets.
5page = client.markets.list_markets(status="ACTIVE", limit=5, sort_by="volume")
6print(f"Open markets ({page.total} total, showing {len(page.items)}):")
7for m in page.items:
8 print(f" • {m.title}")
9 print(f" id={m.id} slug={m.slug} volume={m.total_volume}")

This is lifted verbatim from the tested example sdks/python/examples/quickstart_markets.py. See Quickstart for the full first-call → paper-trade walkthrough.

Place an order idempotently

Preview a fee-inclusive quote, then place the order binding the quote token and an application-owned idempotency key, so a client-side retry hits the server’s replay guarantee instead of double-placing:

quickstart_trade.py
1idempotency_key = f"quickstart-trade-{uuid.uuid4()}"
2result = client.orders.place_order(
3 models.OrderCreate(
4 market_id=market.id,
5 outcome_id=outcome_id,
6 side="BUY",
7 type="MARKET",
8 quantity=ORDER_QTY,
9 preview_quote_token=quote.quote_token,
10 ),
11 include_receipt=True,
12 request_options=RequestOptions(idempotency_key=idempotency_key),
13)

Retries

Configure the retry budget on the client; it retries 5xx and 429, honouring Retry-After:

Client config
1client = DrazillClient(
2 api_key="<DRAZILL_API_KEY>",
3 # Omit to use the default; see "Which environment?" below.
4 base_url="https://staging.drazill.com/api/v1",
5 timeout=10.0, # seconds
6 max_retries=3, # retry on 5xx and 429
7)

Cursor pagination

For endpoints on the signed-cursor envelope, use the exported iterators rather than managing X-Next-Cursor yourself — pass a bound resource method and its params and iterate items directly (see the SDK README’s pagination section). Details in Pagination and the API Reference.

WebSocket

Install the [websocket] extra, then connect, subscribe, and listen. Subscribe to prices:{outcome_id} for price ticks, orderbook:{outcome_id} for depth, and trades:{market_id} for the tape:

Realtime
1import asyncio
2from drazill import DrazillClient
3
4client = DrazillClient(api_key="<DRAZILL_API_KEY>")
5ws = client.ws()
6
7async def stream():
8 await ws.connect()
9 await ws.subscribe("orderbook:OUTCOME_UUID")
10 await ws.subscribe("trades:MARKET_UUID")
11
12 async for event in ws.listen():
13 event_type = event.get("type")
14 channel = event.get("channel", "")
15 data = event.get("data", {})
16 if event_type == "orderbook_update":
17 print(f"Order book update on {channel}: {data}")
18 elif event_type == "trade":
19 print(f"Trade on {channel}: price={data.get('price')}")
20
21asyncio.run(stream())

Verify webhooks

Verify the signature on every inbound webhook before trusting it, then construct the typed event:

webhooks.py
1def handle_webhook(raw_body: bytes, headers: Mapping[str, str], secret: str):
2 if not verify_webhook_signature(raw_body, headers, secret):
3 raise ValueError("Invalid Drazill webhook signature")
4 return construct_webhook_event(raw_body, headers, secret)

The full webhook contract (signing, retries, replay) is under Webhooks.

Request signing

If your key requires HMAC signing, sign "<unix_ts>.<METHOD>.<path>." + body and send the timestamp + signature headers (Authentication):

Signing helper
1import hashlib
2import hmac
3import time
4
5
6def drazill_signature(secret: str, method: str, path: str, body: bytes = b"") -> dict:
7 ts = int(time.time())
8 signed = f"{ts}.{method.upper()}.{path}.".encode() + body
9 digest = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
10 return {"X-Drazill-Timestamp": str(ts), "X-Drazill-Signature": f"v1={digest}"}