Source: https://tesseralytics.dev/sdks

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]"
```

[Get an API key](https://tesseralytics.dev/account) [Raw HTTP reference](https://tesseralytics.dev/api-docs)

## Python

tessera-api 0.1.2[](https://github.com/tesseralytics/python-client "View the source on GitHub")

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[](https://github.com/tesseralytics/rust-client "View the source on GitHub")

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](https://tesseralytics.dev/api-docs)

Every partition is also a plain Parquet download. See the [data catalog](https://tesseralytics.dev/datasets) for the columns.
