Metadata-Version: 2.4
Name: relay-link-py
Version: 0.1.0
Summary: Python client for the Relay Protocol API (relay.link) — fast crosschain bridging and swaps.
Project-URL: Homepage, https://github.com/robertruben98/relay-link-py
Project-URL: Repository, https://github.com/robertruben98/relay-link-py
Project-URL: Documentation, https://docs.relay.link
Project-URL: Issues, https://github.com/robertruben98/relay-link-py/issues
Author: Robert Ruben
License: MIT
License-File: LICENSE
Keywords: bridge,crosschain,defi,ethereum,relay,relay-link,swap,web3
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Provides-Extra: exec
Requires-Dist: solders>=0.18; extra == 'exec'
Requires-Dist: web3>=6.0; extra == 'exec'
Description-Content-Type: text/markdown

# relay-link-py

[![CI](https://github.com/robertruben98/relay-link-py/actions/workflows/ci.yml/badge.svg)](https://github.com/robertruben98/relay-link-py/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/relay-link-py.svg)](https://pypi.org/project/relay-link-py/)
[![Docs](https://img.shields.io/badge/docs-online-blue)](https://robertruben98.github.io/relay-link-py/)
[![Python versions](https://img.shields.io/pypi/pyversions/relay-link-py.svg)](https://pypi.org/project/relay-link-py/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/robertruben98/relay-link-py/blob/main/LICENSE)

A typed Python client for the [Relay Protocol](https://relay.link) API — a fast
crosschain bridge and swap protocol. Sync and async, pydantic v2 models,
`py.typed`, no network needed to import.

- Base URL: `https://api.relay.link` (configurable)
- API reference: <https://docs.relay.link/references/api/overview>
- OpenAPI spec: <https://api.relay.link/documentation/json>

## Install

```bash
pip install relay-link-py
```

Python 3.9+ is supported. The data layer needs only `httpx` and `pydantic`.
Signing the transaction steps a quote returns is out of scope for the core
client; install the optional extra if you want `web3` / `solders` available:

```bash
pip install "relay-link-py[exec]"
```

## Quickstart

```python
from relay_link import RelayClient, TradeType

NATIVE = "0x0000000000000000000000000000000000000000"

with RelayClient() as client:
    # List supported chains (no API key required)
    chains = client.get_chains()
    print(f"{len(chains)} chains supported")

    # Get an executable quote: bridge 1 ETH from Base (8453) to Optimism (10)
    quote = client.get_quote(
        user="0x03508bb71268bba25ecacc8f620e01866650532c",
        origin_chain_id=8453,
        destination_chain_id=10,
        origin_currency=NATIVE,
        destination_currency=NATIVE,
        amount="1000000000000000000",  # 1 ETH in wei
        trade_type=TradeType.EXACT_INPUT,
    )
    for step in quote.steps:
        print(step.kind, step.id, step.action)
```

### Async

```python
import asyncio
from relay_link import AsyncRelayClient, TradeType

async def main():
    async with AsyncRelayClient() as client:
        chains = await client.get_chains()
        print(len(chains))

asyncio.run(main())
```

### Polling execution status

After you submit a step's transaction on-chain, poll its `requestId` until it
reaches a terminal state (`success`, `failure`, or `refund`):

```python
status = client.poll_status(request_id="0x...", interval=2, timeout=120)
print(status.status, status.tx_hashes)
```

## Configuration

```python
client = RelayClient(
    base_url="https://api.relay.link",  # configurable
    api_key="your-key",                 # optional; sent on every request
    api_key_header="x-api-key",         # configurable header name
    timeout=30.0,
    max_retries=3,                      # retries for 429 / 5xx responses
    backoff_base=0.5,                   # exponential backoff base (seconds)
)
```

Quotes are public and need no API key.

### Retries and errors

`429` and transient `5xx` responses are retried up to `max_retries` times with
exponential backoff, honoring a `Retry-After` header when present. The
exception hierarchy:

- `RelayError` — base class for everything this library raises.
  - `RelayAPIError` — non-2xx response; carries `status_code`, `message`, `body`.
    - `RelayRateLimitError` — `429` after retries are exhausted; adds `retry_after`.
  - `RelayConnectionError` — could not reach the API.
  - `RelayTimeoutError` — the request timed out.

## API surface

| Method | Endpoint | Description |
| --- | --- | --- |
| `get_chains()` | `GET /chains` | List supported chains |
| `get_quote(...)` | `POST /quote/v2` | Executable bridge/swap/call quote |
| `get_price(...)` | `POST /price` | Non-executable price estimate |
| `get_status(request_id=...)` | `GET /intents/status/v3` | Execution status |
| `poll_status(request_id=...)` | `GET /intents/status/v3` | Poll to a terminal state |
| `get_requests(...)` | `GET /requests` | List relay requests |
| `get_token_price(address=..., chain_id=...)` | `GET /currencies/token/price` | Token USD price |

`AsyncRelayClient` exposes the same methods as awaitables.

## Development

```bash
uv pip install -e ".[dev]"
ruff check .
mypy --strict src tests
pytest                  # unit tests (network mocked)
pytest -m integration   # one live test against GET /chains
```

## License

MIT
