Client SDKs

Query Hyperliquid data in your language

Two official clients wrap the same REST API in typed bindings, so you never hand-roll HTTP or manage downloads. Both read Parquet straight over the wire — no temp files, projection and predicate pushdown to object storage.

pip install "tessera-api[polars]"

Python

tessera-api 0.1.2

Sync and async clients over httpx, with typed response models. The base install carries only the transport; the extras pull in the engine you actually want, and a missing one raises with the exact install line.

Install
pip install "tessera-api[polars]"

Swap the extra for [duckdb], [pandas] or [all]. Needs Python 3.10+.

Authenticate
export TESSERA_API_KEY="sk_..."

TesseraClient() reads it, or pass api_key=. Set base_url to point at another deployment.

First query

One month of minute candles for one coin, into a pandas or Polars frame.

import tessera

client = tessera.TesseraClient()   # reads $TESSERA_API_KEY

df = client.read("gold_ohlcv_1m", "BTC", "2026-05")
print(df.select("time", "close", "cvd").tail())
SQL instead of DataFrames

Ask for a span of months and query it like a table.

rel = client.to_duckdb("gold_ohlcv_1m", ["BTC", "ETH"], "2026-05")

rel.filter("close > open").aggregate(
    "coin, avg(cvd) AS mean_cvd, count(*) AS n", "coin"
).show()
Lazy over many months

MonthSpan expands to every partition in the range; nothing is read until you collect.

lf = client.scan(
    "gold_ohlcv_1m",
    coin=["BTC", "ETH"],
    month=tessera.MonthSpan("2026-01", "2026-05"),
    columns=["time", "close", "aggressor_delta"],
)

lf.filter(pl.col("cvd") > 0).collect()
Async

AsyncTesseraClient mirrors every method with await.

async with tessera.AsyncTesseraClient() as client:
    df = await client.read("gold_ohlcv_1m", ["BTC", "ETH"], "2026-05")

Rust

tessera-api 0.1.0

The crate is published as tessera-api but imports as tessera. Polars is the default feature; DuckDB is opt-in, and turning it on is what swaps the engine, so Cargo.toml has to say which one you want. Needs Rust 1.85+.

Install
cargo add tessera-api

That gives you Polars. For DuckDB instead, replace the dependency line:

tessera-api = { version = "0.1", default-features = false, features = ["duckdb"] }
First query

Same shape as Python: construct, read, print. The constructor is where the key comes from.

use tessera::TesseraClient;

fn main() -> Result<(), tessera::TesseraError> {
    let client = TesseraClient::new(None)?;   // reads $TESSERA_API_KEY

    let df = client.read("gold_ohlcv_1m", "BTC", "2026-05", None)?;
    println!("{} rows", df.height());
    Ok(())
}

AsyncTesseraClient is the async mirror, for use inside a Tokio runtime — the sync client builds its own runtime for the Polars cloud reads, so it must not be constructed from one.

The same six calls, in both clients

Neither client has per-dataset methods. Everything is addressed by the same (asset, coin, month, columns) tuple, which is why the API surface stays small as datasets are added — call datasets() to discover the names.

Call Python returns Rust returns What it gives you
datasets() DatasetsResponse DatasetsResponse Every dataset your plan can reach, with its visible coins and month range.
partitions(asset, coin?, month?) PartitionsResponse PartitionsResponse Which (coin, month) files exist — the honest way to find the newest month.
scan(asset, coin, month, columns?) pl.LazyFrame LazyFrame A lazy frame over one partition or many. Predicates and projections push down to storage.
read(asset, coin, month, columns?) pl.DataFrame DataFrame The same, eagerly collected.
to_duckdb(asset, coin, month, columns?) DuckDBPyRelation duckdb::Connection The same partition set as SQL — a relation to query, or a connection exposing a view.
download_url(asset, coin, month) DownloadResponse DownloadResponse A short-lived presigned URL for one partition, if you would rather fetch it yourself.

When a span covers more than one partition, both clients append coin and month columns so every row stays attributable. Presigned URLs are short-lived (~15 minutes) — collect a lazy frame promptly, or you will get a presign-expired error rather than stale data.

What your key decides

The client does not hold entitlements; the API applies your plan to every request and filters what comes back. A span that reaches outside your slice is an error, not a silent truncation.

Free No card
  • gold_ohlcv_1m — 1-minute order-flow OHLCV
  • BTC, ETH, SOL & HYPE
  • The trailing month
Pro $29/mo
  • Every dataset, including gold_funding_1h and gold_positioning_1h
  • Every market, including HIP-3
  • Full history — October 2025 onward
Errors you can catch

tessera.TesseraError is the base; ForbiddenError means your plan does not cover the slice, AuthenticationError means the key is wrong or missing, NotFoundError means the partition does not exist, and PresignExpiredError means re-issue the read. Rust returns one TesseraError enum with the same distinctions.

Retries are built in

Both clients retry 429 and 5xx with exponential backoff and honour Retry-After — three attempts by default, 30-second timeout. Python exposes max_retries and timeout; Rust exposes the same on ClientConfig.

Not using Python or Rust?

Both clients are thin wrappers over the same REST API, which is documented from its live OpenAPI schema — generate a typed client in any language, or call it directly.

Browse the API reference

Every partition is also a plain Parquet download. See the data catalog for the columns.