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

# ravnexchange (Python)

> Typed Python client for /api/v1, quote, execute, submit-signature, status, tokens, and chains.

A zero-dependency Python client with the same scope as [`@ravnexchange/sdk`](/sdks/core):
quote, execute, submit-signature, status, tokens, and chains, snake\_cased. This is for a plain,
non-agent Python backend that wants a typed wrapper instead of hand-rolling `POST`/`GET` calls
against the JSON API.

<Note>
  Building an AI agent instead of a backend service? Use RAVN's [MCP server](/ai-agents/mcp-server)
  rather than this package, it's free, already speaks the agent's tool-calling protocol, and covers
  this same surface plus more.
</Note>

## Setup

```bash theme={null}
pip install ravnexchange
```

```python theme={null}
from ravnexchange import RavnClient

client = RavnClient(api_key="rvn_live_your_key_here")  # omit for the anonymous tier
```

<ParamField body="api_key" type="str">Self-serve or enterprise key. Omit for the anonymous tier: it works immediately, at a lower rate limit. See [Authentication](/authentication).</ParamField>
<ParamField body="base_url" type="str">Default `https://app.ravn.exchange/api/v1`.</ParamField>
<ParamField body="urlopen" type="callable">Swap in your own opener for tests or an unusual runtime. Defaults to `urllib.request.urlopen`. Built on the stdlib `urllib.request`, not `requests`, so installing this package adds nothing to your dependency tree.</ParamField>

## Full example

```python theme={null}
from ravnexchange import RavnClient, RavnApiError

client = RavnClient(api_key="rvn_live_your_key_here")

try:
    quote = client.get_quote({
        "inputChainId": 1,
        "outputChainId": 8453,
        "inputToken": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
        "outputToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "inputAmount": "1000000000000000000",
        "userAddress": "0xYourAddress",
        "destinationAddress": "0xYourAddress",
    })
except RavnApiError as e:
    print(e.code, str(e))
else:
    execution = client.execute({"quoteToken": quote["quoteToken"]})
    # branch on execution["executionType"]: TRANSACTION / SIGNATURE / DEPOSIT
```

Request fields are still the camelCase keys the API expects (`inputChainId`, not
`input_chain_id`), the client is a typed wrapper, not a schema translator. Only the method names
themselves are snake\_case.

## Methods

Every method returns the unwrapped `data` from the response envelope, and raises `RavnApiError`
on anything else. Field meanings match the [API Reference](/api-reference/overview) pages linked
from each method.

### get\_quote

```python theme={null}
quote = client.get_quote({
    "inputChainId": 1,
    "outputChainId": -2,
    "inputToken": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
    "outputToken": "So11111111111111111111111111111111111111112",
    "inputAmount": "1000000000000000000",
    "userAddress": "0xYourUser",
    "destinationAddress": "SoYourUser",
})
```

Wraps [`POST /quote`](/api-reference/quote). Omitting `destinationAddress`/`refundAddress`
returns a preview-only quote, so check `quote["executable"]` before calling `execute`. Supports
`sandbox: True` the same as the raw API, see [Sandbox Mode](/sandbox-mode).

### execute

```python theme={null}
execution = client.execute({"quoteToken": quote["quoteToken"]})

if execution["executionType"] == "DEPOSIT":
    ...
elif execution["executionType"] == "SIGNATURE":
    ...
elif execution["executionType"] == "TRANSACTION":
    ...
```

Wraps [`POST /execute`](/api-reference/execute). This client doesn't sign or send anything for
you, branch on `executionType` exactly as in the [Quickstart](/quickstart).

### submit\_signature

```python theme={null}
result = client.submit_signature({
    "quoteToken": quote["quoteToken"],
    "signature": sig,
})
```

Wraps [`POST /submit-signature`](/api-reference/submit-signature). Only for `SIGNATURE`-type
executions. Returns `{"statusRef": ...}` to poll `get_status` with.

### get\_status

```python theme={null}
status = client.get_status(quote["quoteToken"], status_ref)
```

Wraps [`GET /status`](/api-reference/status). `ref` is the `statusRef` from `execute` (for
`DEPOSIT`) or from `submit_signature` (for `SIGNATURE`). Once terminal, a venue that reports it
adds `deliveredAmount` and `txHash` to the result.

### get\_tokens

```python theme={null}
tokens = client.get_tokens(1)  # chainId
```

Wraps [`GET /tokens`](/api-reference/tokens-list) for the given chain.

### get\_chains

```python theme={null}
chains = client.get_chains()
```

Wraps [`GET /chains`](/api-reference/chains). Static, safe to cache client-side.

## Errors

Every non-2xx response, or a 2xx body that still carries an `error` field, raises `RavnApiError`:

```python theme={null}
from ravnexchange import RavnApiError

try:
    client.execute({"quoteToken": quote_token})
except RavnApiError as e:
    print(e.code, e.details, str(e))
```

<ResponseField name="code" type="str">The stable, machine-readable code. See [Errors](/errors) for the full table.</ResponseField>
<ResponseField name="details" type="Any">Present on some codes, for example the failed fields on `INVALID_REQUEST`.</ResponseField>
<ResponseField name="meta" type="dict">`requestId` and `version`, when the API returned an envelope at all.</ResponseField>

<Note>
  The human-readable message isn't a separate `.message` attribute (Python's base `Exception`
  doesn't give you one for free); read it with `str(e)` instead, same value the TS client's
  `err.message` carries.
</Note>
