> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xinsider.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust client

> Call the 0xinsider API from Rust with typed async responses, bounded retries, and a keyless sandbox.

Use the `oxinsider` crate to call the API from a Tokio application. Each operation is an async method with typed parameters and a typed response. The [source repository](https://github.com/0xinsider/0xinsider-rust) contains the crate and examples.

## Install from GitHub

```bash theme={null}
cargo add oxinsider --git https://github.com/0xinsider/0xinsider-rust
cargo add tokio --features macros,rt-multi-thread
```

This command installs the crate from its source repository. You need Rust 1.87 or newer and Tokio. The default TLS implementation is rustls; use `default-features = false, features = ["native-tls"]` to use the platform's TLS implementation.

The package name is `oxinsider`, because Rust crate names cannot start with a digit.

<span id="run-it-without-a-key" />

## Make a request without a key

Use the [sandbox](/sandbox) to return example data without an account or credential.

```rust theme={null}
use oxinsider::{Client, ListLeaderboardParams};

#[tokio::main]
async fn main() -> oxinsider::Result<()> {
    let client = Client::sandbox()?;
    let board = client
        .list_leaderboard(&ListLeaderboardParams::default().limit(5))
        .await?;
    for entry in &board.data {
        // An absent grade is an ungraded wallet, not an F.
        let grade = entry.grade.as_deref().unwrap_or("ungraded");
        println!("{grade:>8}  {}", entry.address);
    }
    Ok(())
}
```

`Client::sandbox()` sends requests to the sandbox origin. See the sandbox page for unsupported operations and the `sandbox_status` query parameter for error examples.

<span id="then-live-data" />

## Switch to live data

Set `OXINSIDER_API_KEY` in your environment, then create the client with:

```rust theme={null}
let client = oxinsider::Client::from_env()?; // reads OXINSIDER_API_KEY
```

The credential can be an API key (`oxi_sk_live_...`) or an OAuth access token (`oxi_at_...`). The API origin is `https://api.0xinsider.com`. Live data requires an active Pro or Max subscription; see [Authentication](/authentication).

Without that environment variable, `Client::from_env()` can still call the [public routes](/authentication#what-needs-a-key). Use `Client::new(key)` to pass a credential directly, or `Client::builder()` to set the origin, timeout, and retry budget.

<span id="calling-an-operation" />

## Pass parameters

Method names use the operation's `operationId` in snake case: `listLeaderboard` becomes `list_leaderboard`.

| Input | How to pass it |
| - | - |
| Path and required query parameters | Pass method arguments, such as `client.get_trader("swisstony", &params)`. |
| Optional query parameters and headers | Use the operation's `...Params` struct and its setters, such as `ListWhaleTradesParams::default().min_grade(Grade::A).limit(100)`. |
| JSON body | Pass the typed `body: &T` argument. |
| `If-None-Match` | Set `if_none_match` on the params. An unchanged response returns `Error::NotModified` with its `etag`. |
| `Idempotency-Key` | Set `idempotency_key` on a supported webhook write. Keep the same key and body when retrying. |

Responses use types in `oxinsider::models`. An absent field is `None`. Unknown response enum values become `Other(String)`, and unknown JSON fields are ignored.

Use `open_stream` for the event stream. The Markdown operations return `String`, and the export download returns a streaming `Download`.

<span id="errors" />

## Handle errors

| Error | Meaning |
| - | - |
| `Error::Api(ApiError)` | The API returned an error status. Read `status`, `code`, `reason`, `message`, `param`, `retry_at`, `retry_after`, and `request_id`. |
| `Error::NotModified` | The API returned `304` because your cached copy is current. |
| `Error::InsecureTransport` | The destination would expose the credential over HTTP. The request was refused. |
| `Error::Transport` | DNS, TLS, a connection failure, or a timeout prevented an HTTP response. |
| `Error::Decode` | A successful body did not match its type. The error includes the failing JSON path and body. |
| `Error::Stream` | The stream returned an invalid frame or failed while being read. |
| `Error::Download` | A file download failed. |
| `Error::Pagination` | A page could not be continued safely. |

Branch on `code` and `reason`, because `message` is text for a person. `ApiError::kind()` groups statuses into values such as `Authentication`, `RateLimited`, and `Server`. See [Errors](/errors) for recovery actions.

<span id="retries" />

## Understand automatic retries

| Setting | Behavior |
| - | - |
| Eligible operations | `GET` requests, read-only batch requests, and supported webhook writes with an `idempotency_key` are eligible. Other writes are sent once. |
| Eligible failures | The client retries `408`, `429`, `502`, `503`, `504`, and connection failures. |
| Wait | It honors `Retry-After`, plus up to 250 ms. Without that header, the delay increases from 500 ms up to 8 seconds. |
| Wait over 60 seconds | The client returns the error with `retry_after` so you can schedule the next attempt. |
| Default budget | The client makes at most 2 retries, with a 30-second deadline per attempt. |
| Disable retries | Set `Client::builder().max_retries(0)`. |

<span id="following-cursors" />

## Read every page

List responses contain `data`, `has_more`, and `next_cursor`. `Pager` requests successive pages until `has_more` is `false`.

```rust theme={null}
use oxinsider::models::Grade;
use oxinsider::pagination::Pager;
use oxinsider::ListWhaleTradesParams;

let mut pager = Pager::new(ListWhaleTradesParams::default().min_grade(Grade::A).limit(100));
while let Some(page) = pager.next_page(async |params| client.list_whale_trades(params).await).await? {
    for trade in &page.data {
        println!("{:?} {:?}", trade.size_usd, trade.market.title);
    }
}
```

A missing continuation cursor or a repeated cursor returns `Error::Pagination`. Treat that as a failure to complete the list. `pagination::collect_all` collects all rows when the result is small enough to keep in memory.

Keep filters unchanged while paging. [Pagination](/concepts/pagination) explains how to handle expired cursors.

<span id="the-live-stream" />

## Read the live stream

`client.open_stream(&StreamOptions)` reads [Stream](/api-reference/endpoint/get-stream) one frame at a time. It holds at most 1 MiB for an undelivered frame.

Set `last_event_id` to the last `seq` you finished processing when reconnecting. A `resync` frame means that point is outside the retained stream history; refetch current data before continuing.

A protocol failure returns `Error::Stream` with `last_seq`. The reader does not reconnect automatically and has no total deadline. Close or drop it when your task ends.

For a finite task, bound the stream operation and its reads in your application. Inside an async function, with the live `client` created above:

```rust theme={null}
let result = tokio::time::timeout(std::time::Duration::from_secs(30), async {
    let mut reader = client.open_stream(&oxinsider::StreamOptions::default()).await?;
    while let Some(frame) = reader.next().await? {
        println!("{:?}", frame.seq);
    }
    oxinsider::Result::Ok(())
}).await;

match result {
    Ok(outcome) => outcome?,
    Err(_) => println!("Stream task reached its 30-second limit."),
}
```

A timeout drops the stream reader and closes the connection. Save your last completed sequence separately if you need to resume.

<span id="credential-safety" />

## Protect the credential

| Case | Behavior |
| - | - |
| Transport | Credentials use HTTPS, or HTTP to a loopback development host. |
| Insecure origin | `build()` and credentialed requests refuse it with `Error::InsecureTransport`. |
| Redirect | Ordinary requests do not follow redirects. Export downloads follow the file redirect without the credential. |
| Sandbox | A live API key is refused in sandbox mode. |
| Debug output | The client redacts its credential. |

<span id="which-document-a-release-implements" />

## Check the generated API document

```rust theme={null}
oxinsider::provenance::OPENAPI_SHA256  // SHA-256 of the source document
oxinsider::provenance::OPERATION_COUNT // operations in that document
oxinsider::provenance::APP_COMMIT      // app commit that changed the document
```

Compare `OPENAPI_SHA256` with the SHA-256 of the [published OpenAPI document](https://0xinsider.com/api/v1/openapi.json). Check the [changelog](/changelog) when the documents differ.

<span id="what-this-client-does-not-do" />

<span id="go-next" />

## Limits

* The client does not place Polymarket orders or hold a wallet key.
* Webhook and export operations can change resources on your 0xinsider account.
* Display numeric fields use `f64`. Keep their precision until display, and use exact decimal strings when a response provides them.
* An absent field remains `None`; do not substitute zero.
* A write that cannot be safely replayed, such as starting an export, is not retried automatically.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.