← tanukitrade.com Open WebApp (opens in a new tab) Start 7-day free trial

Guides

Python client

A small httpx client: gzip, a bearer header, cursor paging, and a wait on 429 and 503. Install with pip install httpx.

tanuki_client.py
import os
import random
import time

import httpx

RETRYABLE = {429, 503}


class Tanuki:
    def __init__(self) -> None:
        self.base = os.environ["TANUKI_BASE_URL"].rstrip("/")
        self.client = httpx.Client(
            timeout=httpx.Timeout(30.0, connect=3.0),
            headers={
                "Authorization": f"Bearer {os.environ['TANUKI_TOKEN']}",
                "Accept": "application/json",
                "Accept-Encoding": "gzip",
                "Content-Type": "application/json",
            },
        )

    def post(self, path: str, body: dict | None = None) -> dict:
        url = self.base + path
        payload = body or {}
        for attempt in range(4):
            response = self.client.post(url, json=payload)
            if response.status_code in RETRYABLE and attempt < 3:
                delay = float(response.headers.get("Retry-After", "1"))
                time.sleep(delay + random.random() * 0.25)
                continue
            response.raise_for_status()
            data = response.json()
            if data.get("ok") is not True:
                code = data.get("error", {}).get("code", "not ok")
                raise RuntimeError(code)
            return data
        raise RuntimeError("retry exhausted")

    def pages(self, path: str, body: dict | None = None):
        payload = dict(body or {})
        payload.pop("cursor", None)
        while True:
            page = self.post(path, payload)
            yield page
            cursor = page["meta"].get("next_cursor")
            if not cursor:
                return
            payload["cursor"] = cursor


api = Tanuki()
expiries = api.post("/api/v1/discovery/expiries", {"symbol": "SPX"})
print(expiries["data"]["items"][0]["expiration_date"])

401, 403, and 422 fall through raise_for_status or the ok check. Retrying a bad token or a bad expiry only spends the auth counter. 429 and 503 wait for Retry-After, then try the same read again. The sleep adds a little jitter so a crowd does not wake on the same millisecond.

Paging

pages sends the same body with cursor set to meta.next_cursor until that field is null. The benchmark list is the one that paged in the captured data.

python
rows = []
for page in api.pages("/api/v1/fundamentals/benchmark-correlation"):
    for item in page["data"]["items"]:
        rows.append(item["symbol"])
print(len(rows), page["meta"]["total"])

Do not put the token in the JSON body. The example token in the shell exports on the overview page is a placeholder.

TanukiTrade API and MCP. Derived options analytics for your code and for agents. Not investment advice. tanukitrade.com