7.1 KiB
cardano-api
A REST API over cardano-db-sync and cardano-node, built with FastAPI, asyncpg, and Redis.
Read queries — address balances, native tokens, transaction history, assets,
stake pools, and blocks — are served directly from the db-sync Postgres
database. Live UTxO lookups, current protocol parameters, and transaction
submission shell out to cardano-cli against a local node socket. Access is
gated by API keys, and keys can optionally be issued self-service by proving
ownership of a wallet that holds a configurable native token (a CIP-8 signature
challenge).
Features
- Address balances, native-token holdings, and transaction history from db-sync
- Block, transaction, asset, and stake-pool lookups
- Live UTxO and protocol-parameter queries via the node
- Transaction submission to the network
- Redis response caching with per-endpoint TTLs
- Tiered API keys with per-tier rate limits
- Optional token-gated, self-service key issuance via CIP-8 wallet signatures
- Strict input validation (bech32 addresses, hex tx hashes / policy IDs) and a body-size cap on transaction submission
Requirements
- A synced
cardano-db-syncPostgres database (thecexplorerschema) - A running
cardano-nodewith a reachable IPC socket — required only for the node-backed endpoints (live UTxOs, protocol params, tx submit) - Redis — rate limiting, response cache, and API-key storage
cardano-clionPATH— the provided Docker image installs it for you
Quick start
The container image bundles cardano-cli; you provide the db-sync database, a
node socket, and Redis. A minimal compose.yml:
services:
cardano-api:
build: .
ports:
- "8765:8765"
environment:
DB_HOST: postgres-dbsync
DB_NAME: cexplorer
DB_USER: dbsync
DB_PASS: ${DB_PASS}
REDIS_HOST: redis
API_MASTER_KEY: ${API_MASTER_KEY}
CARDANO_NODE_SOCKET_PATH: /node-ipc/node.socket
CARDANO_NETWORK: mainnet
volumes:
- /path/to/node-ipc:/node-ipc
redis:
image: redis:7
docker compose up -d --build
The API listens on port 8765. Put it behind your own reverse proxy / TLS
terminator as needed.
To run without Docker:
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8765
(Install cardano-cli separately if you need the node-backed endpoints.)
Configuration
All configuration is via environment variables:
| Variable | Default | Description |
|---|---|---|
DB_HOST |
postgres-dbsync |
db-sync Postgres host |
DB_PORT |
5432 |
db-sync Postgres port |
DB_NAME |
cexplorer |
db-sync database name |
DB_USER |
dbsync |
db-sync database user |
DB_PASS |
(empty) | db-sync database password |
REDIS_HOST |
redis-api |
Redis host |
REDIS_PORT |
6379 |
Redis port |
API_MASTER_KEY |
(empty) | Unrestricted master key for /admin/* |
CARDANO_NODE_SOCKET_PATH |
/node-ipc/node.socket |
Node IPC socket path |
CARDANO_NETWORK |
mainnet |
Cardano network |
TRUSTED_PROXIES in main.py controls which client IPs are allowed to set the
X-Forwarded-For header. It defaults to loopback plus the Docker bridge
gateways; override it to match your own proxy setup.
Authentication & tiers
Pass an API key in the X-API-Key header (preferred) or as an ?api_key= query
parameter. Requests with no key are treated as anonymous.
| Tier | Token balance | Rate (req/min) | tx submit (/min) | Node read | Node submit |
|---|---|---|---|---|---|
| anonymous | 0 | 20 | 0 | no | no |
| standard | ≥ 50 | 100 | 2 | yes | no |
| elevated | ≥ 500 | 1000 | 10 | yes | yes |
| master | n/a | unlimited | unlimited | yes | yes |
Anonymous requests are rate-limited per source IP; authenticated tiers are
rate-limited per key. The master key is supplied out-of-band via API_MASTER_KEY
and can mint non-expiring keys at POST /admin/keys.
Token-gated keys (optional, self-service)
A wallet that holds enough of a configured native token can mint its own API key
by signing a challenge nonce with CIP-8. The gating token's policy ID and the
tier thresholds are constants near the top of main.py — point them at your own
token to use this flow. Token-gated keys expire 48 hours after issue and can be
re-authenticated (which also re-tiers them) via POST /v1/auth/refresh. A
background task re-checks balances every 10 minutes and re-tiers keys in place.
POST /v1/auth/challenge { "address": "addr1..." }
-> { "nonce": "...", "expires_at": "..." }
# sign the nonce with the wallet via CIP-8
POST /v1/auth/verify { "address", "nonce", "signature", "key" }
-> { "api_key": "capi_...", "tier", "trp_balance" }
POST /v1/auth/refresh (X-API-Key header — self-service only)
-> { "tier", "trp_balance", "expires_at", ... }
Endpoints
GET /health
GET /v1/sync/status
GET /v1/block/latest
GET /v1/block/{block_no}
GET /v1/address/{address}/balance
GET /v1/address/{address}/tokens?page=&limit=
GET /v1/address/{address}/transactions?page=&limit=&order=
GET /v1/address/{address}/utxos (standard+)
GET /v1/tx/{tx_hash}
POST /v1/tx/submit (elevated+)
GET /v1/asset/{policy_id}/info?page=&limit=
GET /v1/asset/{policy_id}/{asset_name}/holders?limit=
GET /v1/pool/{pool_id}/info
GET /v1/protocol-params (standard+)
POST /v1/auth/challenge
POST /v1/auth/verify
POST /v1/auth/refresh
POST /admin/keys (master)
DELETE /admin/keys/{key} (master)
GET /admin/keys (master)
POST /admin/refresh-tiers (master)
GET /admin/stats (master)
Caching
Responses are cached in Redis with per-endpoint TTLs (seconds):
balance 60 asset_info 120
tokens 60 pool_info 120
transactions 30 sync_status 5
block_latest 10 protocol_params 300
tx_details 300 utxos 10
Security notes
- API keys are stored as
sha256(key). The raw key is returned exactly once, at creation; lookups, listing, and revocation all operate on the hash. - Every path parameter is validated against a strict regex (bech32 mainnet/testnet addresses, 64-hex tx hashes, 56-hex policy IDs) before any query runs.
POST /v1/tx/submitbodies are capped at 64 KB. The middleware reads the actual request stream, so chunked transfer or a missingContent-Lengthcan't bypass the limit.- CIP-8 verification rejects non-EdDSA algorithms, wrong key length, empty or mismatched payloads, and any key that doesn't hash to the claimed address.
Contributing
Issues and pull requests are welcome. Please keep changes focused, run the app against a local db-sync + node before submitting, and don't commit secrets — a gitleaks workflow scans every push and pull request.
License
Released under the MIT License. See LICENSE.