Skip to main content

drazill

Official alpha Python SDK for the Drazill Prediction Market API. The SDK is MIT licensed while the broader monorepo has its own repository license.

Status: source-available (this repo) — registry publish pending. The drazill package is generated and tested but is not yet published to PyPI. Install it from source (below) for now; the pip install drazill commands throughout this README start working once the owner runs the release pipeline with registry tokens (see ../PUBLISHING.md).

Installation

Until the package is published, install from the monorepo checkout:

pip install -e sdks/python
# with WebSocket support:
pip install -e "sdks/python[websocket]"

Once published to PyPI (registry publish pending), this becomes:

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

Requirements: Python 3.9+

Quick Start

from drazill import DrazillClient

client = DrazillClient(api_key="<DRAZILL_API_KEY>")

# List active markets
result = client.markets.list_markets(status="ACTIVE", limit=10)
for market in result.items:
    print(f"{market.title} - Volume: ${market.total_volume}")

Runnable quickstart examples

Two self-contained scripts under examples/ run end-to-end against staging (safe test data, no real money):

Script What it does Scopes
quickstart_markets.py List open markets → outcomes/prices → live order book → recent trades (read-only) markets:read
quickstart_trade.py Fee-inclusive quote → place a small market BUY → print the execution receipt markets:read, orders:write (fees:read optional)
pip install drazill                # or, from the monorepo: pip install -e sdks/python
export DRAZILL_API_KEY="sk_...your staging key..."
export DRAZILL_BASE_URL="https://staging.drazill.com/api/v1"   # optional; this is the default
python examples/quickstart_markets.py
python examples/quickstart_trade.py                            # DRAZILL_QUOTE_ONLY=1 to preview only

Issuing the demo key. Create a scoped key in the developer portal at /developers/api-keys (the secret is shown once — copy it). Grant only the scopes the script needs. The trade script spends test balance, so fund the key's account first via the staging "Confirm test deposit" flow or the test-balance faucet. Never commit a key — pass it through the environment.

Environment variables both scripts honor: DRAZILL_API_KEY (required), DRAZILL_BASE_URL (defaults to staging). The trade script also honors DRAZILL_MARKET_SLUG, DRAZILL_OUTCOME_ID, DRAZILL_ORDER_QTY (default 1 share), and DRAZILL_QUOTE_ONLY.

API coverage & method naming

Public API surface (generated): 65 resource groups · 460 operations · 864 types. Regenerated from docs/api/openapi.v1.json by sdks/generate_sdks.py; kept honest by the DX-19 drift gate.

The SDK mirrors the entire public API contract — every resource group covering markets, orders, wallet, users, comments, watchlist, notifications, webhooks, disputes, moderation, rewards, compliance, chat, copy-trading, lessons, market intelligence, paper trading, sports, crypto, rank, parlays, conditional/bracket/TWAP/trailing-stop orders, price alerts, referrals, api-keys, security, geo, fees, and more.

Resources and methods are generated from the OpenAPI contract, so each method name matches the API operation it calls (for example GET /markets becomes client.markets.list_markets() and POST /orders becomes client.orders.place_order(...)). Path parameters are positional, query parameters are keyword-only, and request bodies are passed as the first positional argument (a typed model or a plain dict):

client.markets.get_market("market-uuid")                 # path param
client.markets.list_markets(status="ACTIVE", limit=20)    # query params
client.orders.place_order({"market_id": "m", ...})        # request body

Every method exists in both sync (DrazillClient) and async (AsyncDrazillClient) clients.

Maintainers: the resource/type modules are produced by sdks/generate_sdks.py from docs/api/openapi.v1.json. Regenerate with python3 scripts/api/generate_openapi.py && python3 sdks/generate_sdks.py.

Authentication

Create an API key from your Drazill dashboard (requires a registered account).

# API key authentication (recommended)
client = DrazillClient(api_key="<DRAZILL_API_KEY>")

# Bearer token authentication (for Cognito tokens)
client = DrazillClient(bearer_token="eyJhbGciOi...")

Configuration

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
)

Which environment?

This SDK talks to staging by default — test data, test money, no real funds at risk. That is deliberate: production is not a separate live environment yet, so the default stays on staging until the release that ships alongside it.

The base URL resolves in this order:

  1. the base_url= argument, if you pass one;
  2. the DRAZILL_BASE_URL environment variable;
  3. https://staging.drazill.com/api/v1 (the default).

The realtime URL resolves the same way via DRAZILL_WS_URL, defaulting to wss://staging.drazill.com/ws. client.ws() derives its URL from whatever the client resolved, so the two can never disagree.

export DRAZILL_BASE_URL="http://localhost:8000/api/v1"   # e.g. a local backend

You can point at any environment explicitly, but no environment other than staging is ever selected for you.

Async Client

from drazill import AsyncDrazillClient

async with AsyncDrazillClient(api_key="<DRAZILL_API_KEY>") as client:
    markets = await client.markets.list_markets(status="ACTIVE")
    print(f"Found {markets.total} markets")

Request signing (optional, FP-107)

If your key was created with signing_required, every request must carry an HMAC signature. Sign "<unix_ts>.<METHOD>.<path>." + body with the signing secret returned once at key creation, and send X-Drazill-Timestamp + X-Drazill-Signature: v1=<hex>:

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}"}

For safe automated testing, create a key with the env:paper scope — real-money actions are blocked and you target the paper-trading endpoints.

Markets

# List markets with filters
result = client.markets.list_markets(
    status="ACTIVE",
    q="bitcoin",
    sort_by="total_volume",
    limit=20,
)

# Get a single market
market = client.markets.get_market("market-uuid")
market = client.markets.get_market_by_slug("will-bitcoin-reach-100k")

# Featured and trending
featured = client.markets.get_featured_markets()
trending = client.markets.get_trending_markets()
for_you = client.markets.get_for_you_markets()

# Categories, tags, stats
categories = client.markets.list_categories()
tags = client.markets.get_popular_tags()
stats = client.markets.get_market_stats("market-uuid")

# Trades and price history
trades = client.markets.get_market_trades("market-uuid")
history = client.markets.get_market_price_history("market-uuid", interval="1h")

Orders

# Place a limit order (body may be a dict or a drazill.types model)
order = client.orders.place_order({
    "market_id": "market-uuid",
    "outcome_id": "outcome-uuid",
    "side": "BUY",
    "type": "LIMIT",
    "price": "0.65",
    "quantity": "10",
})

# Place a market order and ask for an execution receipt
order = client.orders.place_order(
    {"market_id": "m", "outcome_id": "o", "side": "BUY", "type": "MARKET", "quantity": "10"},
    include_receipt=True,
)

# List your orders / open orders
result = client.orders.list_my_orders(status="OPEN")
open_orders = client.orders.list_open_orders()

# Cancel
client.orders.cancel_order("order-uuid")
client.orders.cancel_all_orders()

# Order book and price impact
book = client.orders.get_order_book("outcome-uuid")
impact = client.orders.get_price_impact("outcome-uuid", side="BUY", quantity="100")

# Pre-trade quote and your trade history
quote = client.orders.preview_order_quote({"outcome_id": "o", "side": "BUY", "quantity": "10"})
my_trades = client.orders.get_my_trades()

Wallet

wallet = client.wallet.get_wallet()
print(f"Balance: ${wallet.balance} CAD")

portfolio = client.wallet.get_portfolio_summary()
positions = client.wallet.list_positions()
txs = client.wallet.list_transactions(type="TRADE_BUY", limit=50)

# Payments
methods = client.wallet.list_payment_methods()
client.wallet.create_deposit({"amount": "100.00", "payment_method_id": "pm_x"})
client.wallet.create_withdrawal({"amount": "50.00"})

# Analytics
analytics = client.wallet.get_portfolio_analytics()
pnl = client.wallet.get_pnl_today()

Users

me = client.users.get_my_profile()
client.users.update_my_profile({"bio": "Prediction market enthusiast"})
stats = client.users.get_my_stats()

profile = client.users.get_user_profile("username")
leaders = client.users.get_leaderboard()

client.users.follow_user("username")
client.users.unfollow_user("username")
results = client.users.search_users(q="john")

# Avatar upload (multipart) — pass file bytes or an (filename, bytes, content-type) tuple
with open("avatar.png", "rb") as fh:
    client.users.upload_avatar(fh.read())

Comments, Watchlist & Notifications

# Comments
comments = client.comments.get_comments("market-uuid")
client.comments.create_comment({"market_id": "market-uuid", "content": "Interesting!"})
client.comments.toggle_like_comment("comment-uuid")

# Watchlist
items = client.watchlist.get_watchlist()
client.watchlist.add_to_watchlist("market-uuid")
result = client.watchlist.toggle_watchlist("market-uuid")
print(f"Watchlisted: {result.is_watchlisted}")

# Notifications
notifs = client.notifications.get_notifications()
count = client.notifications.get_notification_count()
client.notifications.mark_notifications_read({"notification_ids": ["notification-uuid"]})
client.notifications.mark_all_notifications_read()

Pagination

List endpoints return a typed page object (items, total, page, pages); cursor endpoints (suffixed _cursor) return items plus next_cursor. Rather than hand-roll the loop, use the exported iterators — pass a bound resource method and its params and iterate items directly:

from drazill import iter_cursor, iter_offset

# Cursor pagination (the platform's canonical signed-cursor scheme)
for market in iter_cursor(client.markets.list_markets_cursor, limit=50):
    print(market.title)

# Offset pagination
for market in iter_offset(client.markets.list_markets, status="ACTIVE", limit=50):
    print(market.title)

Use aiter_cursor / aiter_offset with async for on the async client. Each iterator fetches one page at a time and stops when the data runs out.

Webhooks

Create API keys with webhooks:read and webhooks:write scopes before using the webhook management API.

from drazill import construct_webhook_event, verify_webhook_signature

# Register an endpoint. The whsec_ secret is returned only once.
endpoint = client.webhooks.create_webhook({
    "url": "https://example.com/drazill/webhooks",
    "subscribed_events": ["order.filled", "trade.executed", "wallet.deposit.completed"],
})
print(endpoint.secret)

# Send sandbox deliveries without mutating market/order/wallet/reward state.
client.webhooks.send_webhook_test_event(endpoint.id)
client.webhooks.create_sandbox_webhook_event({
    "endpoint_id": endpoint.id,
    "event_type": "order.filled",
    "data": {"order_id": "00000000-0000-0000-0000-000000000000"},
})

# Inspect and replay failed deliveries.
failures = client.webhooks.list_endpoint_deliveries(endpoint.id, status="FAILED")
for delivery in failures.items:
    client.webhooks.replay_webhook_delivery(delivery.id)

# Verify an incoming request before parsing.
is_valid = verify_webhook_signature(raw_body, headers, endpoint.secret)
if not is_valid:
    raise ValueError("Invalid Drazill webhook signature")

event = construct_webhook_event(raw_body, headers, endpoint.secret)
print(event.type, event.data)

WebSocket (Real-Time Data)

Requires pip install drazill[websocket].

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())

Available Channels

Channel Description
orderbook:{outcome_id} Live order book depth for an outcome
trades:{market_id} Trade executions on a market
prices:{outcome_id} Price tick updates
market:{market_id} Market status changes and resolution
user:{user_id} Private notifications and order fills (requires auth)

Error Handling

from drazill import (
    ApiError,
    AuthenticationError,
    RateLimitError,
    ValidationError,
    NetworkError,
)

try:
    order = client.orders.place_order({...})
except ValidationError as e:
    print(f"Validation errors: {e.errors}")
except AuthenticationError:
    print("Bad credentials")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except ApiError as e:
    print(f"API error {e.status}: {e.code} - {e.message}")
except NetworkError as e:
    print(f"Network error: {e.message}")

Type Safety

All responses are Pydantic models with full type hints. Generated request/ response models live under drazill.types:

from drazill.types import models

result = client.markets.list_markets(status="ACTIVE")   # typed page object
market: models.MarketListResponse = result.items[0]
print(market.title)  # IDE autocomplete works here

Context Manager

# Sync
with DrazillClient(api_key="<DRAZILL_API_KEY>") as client:
    markets = client.markets.list_markets()

# Async
async with AsyncDrazillClient(api_key="<DRAZILL_API_KEY>") as client:
    markets = await client.markets.list_markets()

License

MIT

Release files for drazill 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for drazill 0.2.0
File Size Uploaded
drazill-0.2.0.tar.gz 122.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for drazill 0.2.0
File Interpreter ABI Platform
drazill-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 258.1 kB

Release files / drazill-0.2.0.tar.gz

Download URL drazill-0.2.0.tar.gz
Size 122.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7057ac606b786ee98a2c45f71a92c2fce597f3e3cb929d5af5d4a113e6e762d4
BLAKE2b-256 checksum
How to use checksums
34337a1d29145a7703eb12b9728b15cee0762bcba59a78fa100901291e910cfe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / drazill-0.2.0-py3-none-any.whl

Download URL drazill-0.2.0-py3-none-any.whl
Size 135.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
516d5a00734b01195195d0c9632bdfdcaf6f5df21c661bd9be0ebaf2dc9faa7c
BLAKE2b-256 checksum
How to use checksums
fad825909d2e44ecc3e17d75cffe6a1769a39dae212e1fa0a300561ae11732e9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release 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