Python client for the Pyth Network Hermes price-oracle API (sync + async, SSE streaming).
Project description
pyth-hermes
A typed Python client for the Pyth Network Hermes price-oracle API. Sync and async clients, Pydantic v2 models, Server-Sent-Events price streaming with auto-reconnect, and a Decimal price helper.
- Sync (
HermesClient) and async (AsyncHermesClient) APIs overhttpx - SSE streaming with reconnect + backoff
- Graceful 429 rate-limit handling (retries that respect the 60s window)
- Configurable
base_url(production, beta, or paid providers) and optional API key from day one mypy --strictclean, fully type-hinted, shipspy.typed
Install
pip install pyth-hermes
# with the optional pandas helper:
pip install "pyth-hermes[pandas]"
Quickstart — BTC/USD price in under 5 lines
from pyth_hermes import HermesClient
client = HermesClient()
feed_id = client.get_feed_id("Crypto.BTC/USD") # exact-symbol lookup
print(client.get_price_decimal(feed_id)) # -> Decimal("63952.82...")
Async + streaming
import asyncio
from pyth_hermes import AsyncHermesClient
BTC = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
async def main():
async with AsyncHermesClient() as client:
async for update in client.stream_prices([BTC]):
print(update.parsed[0].to_decimal())
asyncio.run(main())
Historical price
resp = client.get_price_at(1718900000, [BTC]) # unix timestamp
print(resp.parsed[0].to_decimal())
pandas
from pyth_hermes.pandas import updates_to_dataframe
df = updates_to_dataframe([client.get_latest_price([BTC])])
Prices and exponents
Pyth returns integer prices plus an exponent. The real value is price * 10**expo, computed exactly as a Decimal:
from pyth_hermes import price_to_decimal
price_to_decimal(6395282153102, -8) # Decimal("63952.82153102")
RpcPrice.to_decimal() and ParsedPriceUpdate.to_decimal() are convenience wrappers.
Finding feed ids — use the EXACT symbol
/v2/price_feeds?query=btc returns deprecated / variant feeds (e.g. MBTC, XBTC) before the canonical one and matches substrings. get_feed_id() therefore matches on exact attributes.symbol:
client.get_feed_id("Crypto.BTC/USD")
# -> "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
🔴 Authentication (changes 2026-07-31)
Today the public endpoint needs no API key. From 2026-07-31 an API key becomes mandatory. This client accepts one from day one — pass it now to be ready:
client = HermesClient(api_key="YOUR_KEY") # default: Authorization: Bearer YOUR_KEY
The exact header is not finalized publicly, so both the header name and scheme are configurable:
HermesClient(api_key="KEY", api_key_header="X-Api-Key", api_key_scheme="") # -> X-Api-Key: KEY
Endpoints / base URLs
from pyth_hermes import HermesClient
HermesClient() # production: https://hermes.pyth.network
HermesClient(base_url="https://hermes-beta.pyth.network") # beta
HermesClient(base_url="https://your-paid-provider.example") # Triton / P2P / extrnode / Liquify
You may also inject your own preconfigured httpx.Client / httpx.AsyncClient via client=... (e.g. for custom transports, proxies, or connection pools). In that case the request host is taken from your client, so set base_url on the client itself — passing both base_url= and client= raises a UserWarning because the constructor's base_url would be a no-op. The api_key is still applied per-request, so it works with an injected client.
import httpx
from pyth_hermes import HermesClient
http = httpx.Client(base_url="https://your-paid-provider.example", proxy="http://localhost:8080")
client = HermesClient(api_key="KEY", client=http) # base_url comes from `http`
Rate limits
The public endpoint allows 10 requests / 10 seconds per IP. Exceeding it returns HTTP 429 for the next 60 seconds. The client retries 429 and 5xx responses with exponential backoff + jitter, honoring any Retry-After header and never exceeding the 60s rate-limit window per delay. Tune via max_retries, backoff_base, backoff_cap.
Not implemented
The TWAP endpoints (/v2/updates/twap/...) are intentionally omitted — the API returns HTTP 400 "deprecated and no longer available".
Development
pip install -e ".[dev]"
pytest # unit tests (no network)
pytest -m integration # live smoke tests against production
mypy
ruff check .
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyth_hermes-0.1.1.tar.gz.
File metadata
- Download URL: pyth_hermes-0.1.1.tar.gz
- Upload date:
- Size: 21.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db022f6d588e4ef593c4e89db34056a8c242d443fa4d42157a35968d8fe18a9f
|
|
| MD5 |
86831e769134cbdc137a3296304a64c7
|
|
| BLAKE2b-256 |
76922255f959c9b196ffd9cb696fac018e38c6d3f10ef9cc37061b28557ed6e6
|
File details
Details for the file pyth_hermes-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pyth_hermes-0.1.1-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
327381b82262ba59a26d88b641ba348ec9faea129f4125bc017fcb5534b92441
|
|
| MD5 |
46d245688327c057543ec963cad70eb4
|
|
| BLAKE2b-256 |
b13ac49feb6fa1b8b25f5a8ff040e318d9d83ca66bff75e07c023794a63c6a40
|