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
client = DrazillClient(api_key=API_KEY, base_url=BASE_URL)
print(f"→ Drazill API @ {BASE_URL}\n")
# 1) List a page of open markets.
page = client.markets.list_markets(status="ACTIVE", limit=5, sort_by="volume")
print(f"Open markets ({page.total} total, showing {len(page.items)}):")
for m in page.items:
print(f" • {m.title}")
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
idempotency_key = f"quickstart-trade-{uuid.uuid4()}"
result = client.orders.place_order(
models.OrderCreate(
market_id=market.id,
outcome_id=outcome_id,
side="BUY",
type="MARKET",
quantity=ORDER_QTY,
preview_quote_token=quote.quote_token,
),
include_receipt=True,
request_options=RequestOptions(idempotency_key=idempotency_key),
)

Retries

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

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

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
import asyncio
from drazill import DrazillClient
client = DrazillClient(api_key="<DRAZILL_API_KEY>")
ws = client.ws()
async def stream():
await ws.connect()
await ws.subscribe("orderbook:OUTCOME_UUID")
await ws.subscribe("trades:MARKET_UUID")
async for event in ws.listen():
event_type = event.get("type")
channel = event.get("channel", "")
data = event.get("data", {})
if event_type == "orderbook_update":
print(f"Order book update on {channel}: {data}")
elif event_type == "trade":
print(f"Trade on {channel}: price={data.get('price')}")
asyncio.run(stream())

Verify webhooks

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

webhooks.py
def handle_webhook(raw_body: bytes, headers: Mapping[str, str], secret: str):
if not verify_webhook_signature(raw_body, headers, secret):
raise ValueError("Invalid Drazill webhook signature")
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
import hashlib
import hmac
import time
def drazill_signature(secret: str, method: str, path: str, body: bytes = b"") -> dict:
ts = int(time.time())
signed = f"{ts}.{method.upper()}.{path}.".encode() + body
digest = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return {"X-Drazill-Timestamp": str(ts), "X-Drazill-Signature": f"v1={digest}"}