meteora-py
Typed, read-only Python client for the Meteora DLMM Data API on
Solana — list liquidity pools, read per-pool metrics (TVL, volume, fees, APR/APY), browse
token-pair pool groups, and pull OHLCV candles and historical volume. Sync and async,
backed by httpx with pydantic
v2 models.
The Meteora DLMM Data API is public and keyless (rate-limited to 30 requests/second). This library covers the read endpoints; it does not build or sign transactions.
Install
pip install meteora-py
Quickstart
from meteora import MeteoraClient
with MeteoraClient() as client:
# Protocol-wide aggregates
stats = client.get_protocol_metrics()
print(f"TVL ${stats.total_tvl:,.0f} across {stats.total_pools:,} pools")
# Page through pools (sorted server-side; sort_by is "<field>:<asc|desc>")
page = client.get_pools(page=1, page_size=10, sort_by="tvl:desc")
for pool in page.data:
print(f"{pool.name:16} TVL ${pool.tvl:,.0f} APR {pool.apr:.2%} APY {pool.apy:.0%}")
# A single pool by its on-chain address
sol_usdc = client.get_pool("5rCf1DM8LjKTw4YqhnoLcngyZYeNnQqztScTogYHAS6")
print(sol_usdc.token_x.symbol, sol_usdc.token_y.symbol, sol_usdc.current_price)
# OHLCV candles and historical volume
candles = client.get_pool_ohlcv(sol_usdc.address)
history = client.get_pool_volume_history(sol_usdc.address)
print(candles.data[-1].close, history.data[-1].fees)
Async
import asyncio
from meteora import AsyncMeteoraClient
async def main() -> None:
async with AsyncMeteoraClient() as client:
stats = await client.get_protocol_metrics()
print(stats.total_pools)
asyncio.run(main())
See examples/ for runnable scripts.
Covered endpoints
Base URL: https://dlmm.datapi.meteora.ag (override via MeteoraClient(base_url=...)).
| Method | Endpoint | Client method |
|---|---|---|
GET |
/stats/protocol_metrics |
get_protocol_metrics() |
GET |
/pools |
get_pools(page, page_size, query, sort_by, filter_by)* |
GET |
/pools/{address} |
get_pool(address) |
GET |
/pools/groups |
get_pool_groups(page, page_size) |
GET |
/pools/{address}/ohlcv |
get_pool_ohlcv(address, from_, to, resolution) |
GET |
/pools/{address}/volume/history |
get_pool_volume_history(address) |
*sort_by is a "<field>:<asc|desc>" expression (e.g. "tvl:desc"); a bare field name is
rejected with HTTP 400. filter_by is a "<field>:<value>" filter expression passed through
verbatim. Both were confirmed against the live API.
All endpoints were discovered and verified live against the public API. The Meteora docs also
expose /portfolio, /positions, /wallets, and limit-order endpoints, plus separate DAMM v1/v2
APIs — these are out of scope for v1 and may follow in a later release.
Error handling
Every method raises MeteoraAPIError (a subclass of MeteoraError) on a non-2xx response.
Network-level failures propagate as the underlying httpx exceptions.
from meteora import MeteoraClient, MeteoraAPIError
with MeteoraClient() as client:
try:
client.get_pool("not-a-real-address")
except MeteoraAPIError as exc:
print(exc.status_code, exc.message)
Models are configured with extra="allow", so fields Meteora adds over time are preserved
rather than rejected.
Development
pip install -e ".[dev]"
ruff check . && ruff format --check .
mypy
pytest -q # unit tests (respx-mocked, no network)
pytest -m integration # one live test against the real API