> ## 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.

# Python client

> Call the 0xinsider API from Python with typed methods, error handling, pagination, and a keyless sandbox.

Use the official Python package to read 0xinsider data from a script or service. Install `0xinsider` and import `oxinsider`. The [source repository](https://github.com/0xinsider/0xinsider-python) contains the client and examples.

## Install

```bash theme={null}
pip install 0xinsider
```

You need Python 3.9 or newer. The package uses `httpx` for HTTP requests and includes its type annotations.

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

## Make a request without a key

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

```python theme={null}
import oxinsider

with oxinsider.Client.sandbox() as client:
    page = client.list_leaderboard(limit=5)
    for entry in page["data"]:
        print(entry)
```

`Client.sandbox()` uses `https://0xinsider.com/sandbox` and sends no credential. See the sandbox page for supported operations. Add `sandbox_status` to request a documented error example:

```python theme={null}
with oxinsider.Client.sandbox() as client:
    try:
        client.request("GET", "/api/v1/leaderboard", query={"sandbox_status": 429})
    except oxinsider.RateLimitedError as error:
        print(error.code, error.retry_after)  # rate_limited 60
```

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

## Switch to live data

Set `OXINSIDER_API_KEY` in your environment. `Client()` reads it automatically.

```python theme={null}
import oxinsider

client = oxinsider.Client()  # reads OXINSIDER_API_KEY
trader = client.get_trader("swisstony", expand=["strategy", "categories"])
print(trader["data"].get("grade"), trader["data"]["pnl"].get("realized"))
```

You can also pass `api_key=` from a secret manager. Use an API key or an OAuth access token linked to an account with an active Pro or Max subscription; see [Authentication](/authentication).

The same methods work in the sandbox and against live data. The remaining snippets assume `client` is the live client created above. Use a `with` block or call `client.close()` when your application finishes.

<span id="keep-financial-values-exact" />

## Keep money values exact

Trader and position responses can include an `exact` block containing decimal strings. Parse a value directly with `Decimal` when you need exact arithmetic. Converting through `float` first can lose precision.

```python theme={null}
from decimal import Decimal

profile = client.get_trader("swisstony")
realized_atom = profile["data"]["pnl"].get("exact", {}).get("realized")
if realized_atom is not None:
    realized_usd = Decimal(realized_atom["value"])
    print(realized_usd + Decimal("0.01"))

positions = client.list_positions(wallet=["swisstony"], min_size=0)
current_usd = None
if positions["data"]:
    current_atom = positions["data"][0].get("exact", {}).get("current_value_usd")
    if current_atom is not None:
        current_usd = Decimal(current_atom["value"])
```

Each value includes `unit`, `scale`, and `basis`. The block or one of its fields can be absent when the source is unavailable. An absent value is not zero.

<span id="method-names" />

## Find a method

Method names use the OpenAPI `operationId` in snake case. For example, `listLeaderboard` becomes `list_leaderboard`, and `getMarketFlow` becomes `get_market_flow`.

Most methods return the decoded JSON body, including the API envelope. Markdown methods return strings, and download methods return a streaming `Download`.

```python theme={null}
oxinsider.OPERATIONS        # operations this release implements
oxinsider.OPERATION_COUNT   # number of operations in its source document
oxinsider.OPENAPI_SHA256    # SHA-256 of that document
oxinsider.APP_COMMIT        # app commit that last changed that document
```

Compare `OPENAPI_SHA256` with the SHA-256 of the [published OpenAPI document](https://0xinsider.com/api/v1/openapi.json). Check the [changelog](/changelog) and upgrade when you need a newer operation or type.

<span id="types" />

## Read optional fields

Your editor uses `TypedDict` types from `oxinsider.types` for request bodies and responses. The client returns ordinary decoded dictionaries; it does not validate or convert them into model objects at runtime.

```python theme={null}
trader = client.get_trader("swisstony")          # GetTraderResponse
trader["data"]["pnl"].get("realized")            # float | None
client.list_positions(min_grade="A")             # min_grade takes only a real grade
client.get_trader_context_markdown("swisstony")  # str, not an envelope
client.with_response.get_trader("swisstony")     # ApiResponse[GetTraderResponse]
```

| API field | Python type and reading rule |
| - | - |
| Required | The key is always present in the documented response. |
| Optional | Use `.get()`, such as `trader["data"].get("grade")`. |
| Nullable | The key can contain `None`, corresponding to JSON `null`. |
| Response enum | The type accepts documented values and new strings added by the API. |
| Request enum | The type accepts only the documented choices. |
| Constant discriminator | A check such as `entry["state"] == "opened"` narrows the union to that shape. |

An omitted key and a `null` value are different, and neither means zero. A filter can guarantee a value without changing the generated type; read optional fields with `.get()` even when the filter normally supplies them.

New response keys remain in the dictionary even if your installed types do not know them. Upgrade the package, or use the low-level `client.request(...)` when you need an untyped call. Resolving annotations with `typing.get_type_hints` requires Python 3.10 or newer.

<span id="pagination" />

## Read every page

`paginate` yields items and follows `next_cursor` while keeping your filters unchanged. Use the canonical large-trade operation for new code:

```python theme={null}
for trade in client.paginate("list_large_trades", min_grade="A", limit=100):
    print(trade["size_usd"], trade["market"]["title"])
```

The client checks a page before yielding it. A malformed list, invalid `data`, missing continuation cursor, or repeated cursor raises `PaginationError`.

```python theme={null}
try:
    for trade in client.paginate("list_large_trades", min_grade="A"):
        print(trade)
except oxinsider.PaginationError as error:
    resume = oxinsider.pagination_checkpoint(error)
    print(error.reason, error.request_id, resume.pages_fetched)
```

Read `error.reason` to identify the problem. `pagination_checkpoint(error)` returns the last paging position when one exists; it does not mean the list was completed.

Pass a `PaginationProgress()` object as `progress=` to retain the position during a normal run. Resume with `progress.cursor` to fetch the last page again, or `progress.next_cursor` after processing the whole page. `progress.stopped_by` distinguishes the end of the list from a limit you supplied.

See [Pagination](/concepts/pagination) for filter changes and expired cursors.

<span id="headers-on-a-successful-call" />

## Read headers and cache validators

Use `client.with_response.<method>(...)` when you need the body and headers together:

```python theme={null}
resp = client.with_response.list_whale_trades(min_grade="A")
resp.data          # the same body the plain method returns
resp.etag          # pass to if_none_match= on the next read
resp.rate_limit    # Budget: limit, remaining, reset_at, reset_after
resp.request_id    # quote this when you report a bad response
```

`ApiResponse.data` is the same body the plain method returns. The wrapper also exposes `monthly_quota`, `batch_rate_limit`, `request_cost`, `retry_after`, and `header(name)`.

Send `.etag` back as `if_none_match=` to revalidate a cached response. A `304` returns a `not_modified` result, so keep your cached body. An absent budget header is `None`; it does not mean the budget is exhausted.

<span id="errors" />

## Handle failures

```python theme={null}
try:
    client.get_trader("0x0000000000000000000000000000000000000000")
except oxinsider.SubscriptionRequiredError:
    print("Paid access is not active on this key")
except oxinsider.RateLimitedError as error:
    print(error.status, error.code, error.retry_after)
```

An API error raises an exception with `status`, `code`, and `retry_after`. All client errors inherit `OxinsiderError`; errors carrying an API body also inherit `OxinsiderApiError`.

| Exception | Meaning |
| - | - |
| `BadRequestError` | The API returned `400`. Fix the request. |
| `AuthenticationError` | The API returned `401`. Check the credential. |
| `SubscriptionRequiredError` | The API returned `402`. Check Pro or Max access. |
| `PermissionDeniedError` | The API returned `403`. Check access or OAuth scopes. |
| `NotFoundError` | The API returned `404`. Check the resource identifier. |
| `RateLimitedError` | The API returned `429`. Read its reason and retry time. |
| `ServerError` | The API returned `500`, `502`, `503`, or `504`. |
| `OxinsiderConnectionError` | No HTTP response was received. |
| `InsecureTransportError` | The destination would expose the credential over HTTP. |
| `DownloadError` | A redirected file download failed. |
| `PaginationError` | A page could not be continued safely. |

[Errors](/errors) explains the API's `code` and `reason` values.

<span id="other-behavior-worth-knowing" />

## Request behavior

| Feature | Behavior |
| - | - |
| Timeout | The default is 30 seconds per call. Set `timeout=` on the client to change it. |
| Conditional request | `if_none_match=` returns a `not_modified` result on `304`. |
| Webhook write | Use `idempotency_key=` where the operation supports it. |
| Raw stream | `client.request("GET", "/api/v1/stream", stream=True)` returns an open `httpx.Response`; close it after reading. |
| Download | Redirect-backed operations return a streaming `Download`. The client follows the redirect once without sending the credential to the file host. |
| Async code | `AsyncClient` provides async operation methods and a resumable stream reader. See the [client README](https://github.com/0xinsider/0xinsider-python#async-client). |

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

<span id="go-next" />

## Read an async stream for a finite task

`AsyncClient.stream()` reconnects and resumes from the last received event ID. For a task with a time limit, cancel the read through `asyncio.wait_for`; the client closes the connection on cancellation. This example requires Python 3.9 or newer:

```python theme={null}
import asyncio
import oxinsider


async def read_stream():
    async with oxinsider.AsyncClient() as async_client:
        async for event in async_client.stream():
            print(event)


async def main():
    try:
        await asyncio.wait_for(read_stream(), timeout=30)
    except asyncio.TimeoutError:
        print("Stream task reached its 30-second limit.")


asyncio.run(main())
```

Automatic resume tracks received events. If processing can fail, save your own completed-event checkpoint before reconnecting; see [Stream](/api-reference/endpoint/get-stream).

## Limits

* The client does not place Polymarket orders or hold a wallet key.
* Webhook and export methods can change resources on your 0xinsider account.
* Missing values remain missing. Keep numeric precision until display and use exact decimal strings for arithmetic when available.
* A package release implements its recorded API document. Upgrade to get newly generated methods and types.


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