REST API
Run your own blockchain data API. ChainETL includes a FastAPI server that queries EVM nodes in real-time — no database, no third-party service, no API keys. Self-hosted and fully open source.
Interactive docs available at /docs (Swagger) and /redoc (ReDoc) when running locally.
4 EVM
Chains
Self-hosted
Auth
JSON
Format
Quick Start
# Start the API server
chainetl serve --port 8000
# Or with uvicorn directly
uvicorn chainetl.api:app --host 0.0.0.0 --port 8000# Query the API
curl http://localhost:8000/chains/ethereum/blocks/latest
# Get a specific block with full data
curl http://localhost:8000/chains/ethereum/blocks/18000000/fullResponse Headers
Every response includes:
| Header | Example | Description |
|---|---|---|
X-Request-Id | a1b2c3d4 | Unique request identifier for debugging |
X-Response-Time | 42.5ms | Server-side processing time |
X-Powered-By | ChainETL | Service identifier |
Access-Control-Allow-Origin | * | CORS enabled for all origins |
Endpoints
Base URL: http://localhost:8000
GET
/Health check and API info
Response
{
"status": "ok",
"version": "0.2.0",
"chains": 4
}GET
/chainsList all supported chains with latest block numbers
Response
[
{
"chain": "ethereum",
"latest_block": 19234567,
"rpc_url": "https://eth.llamarpc.com"
},
{
"chain": "polygon",
"latest_block": 55000000,
"rpc_url": "https://polygon-rpc.com"
}
]GET
/chains/{chain}Get chain info including latest block number
Parameters
chain | string | Chain name (ethereum, base, polygon, arbitrum) |
Response
{
"chain": "ethereum",
"latest_block": 19234567,
"rpc_url": "https://eth.llamarpc.com"
}GET
/chains/{chain}/blocks/latestGet the most recent block
Parameters
chain | string | Chain name |
Response
{
"chain": "ethereum",
"number": 19234567,
"hash": "0xabc...def",
"parent_hash": "0x123...456",
"timestamp": 1700000000,
"transaction_count": 142
}GET
/chains/{chain}/blocks/{block_number}Get a specific block by number
Parameters
chain | string | Chain name |
block_number | integer | Block number |
Response
{
"chain": "ethereum",
"number": 18000000,
"hash": "0xabc...def",
"parent_hash": "0x123...456",
"timestamp": 1693526400,
"transaction_count": 98
}GET
/chains/{chain}/blocks/{block_number}/transactionsGet all transactions in a block
Parameters
chain | string | Chain name |
block_number | integer | Block number |
Response
[
{
"hash": "0xf1a2...b3c4",
"block_number": 18000000,
"from_address": "0x1111...1111",
"to_address": "0x2222...2222",
"value": "1000000000000000000",
"gas": 21000,
"gas_price": 30000000000,
"transaction_type": 2
}
]GET
/chains/{chain}/blocks/{block_number}/fullGet a block with all transactions, logs, and token transfers
Parameters
chain | string | Chain name |
block_number | integer | Block number |
Response
{
"chain": "ethereum",
"number": 18000000,
"hash": "0xabc...def",
"parent_hash": "0x123...456",
"timestamp": 1693526400,
"transaction_count": 98,
"transactions": [
{
"hash": "0xf1a2...b3c4",
"block_number": 18000000,
"from_address": "0x1111...1111",
"to_address": "0x2222...2222",
"value": "1000000000000000000",
"gas": 21000,
"gas_price": 30000000000,
"transaction_type": 2
}
],
"logs_count": 245,
"token_transfers": [
{
"transaction_hash": "0xf1a2...b3c4",
"log_index": 0,
"token_address": "0xdAC1...1eC7",
"token_standard": "ERC-20",
"from_address": "0x1111...1111",
"to_address": "0x2222...2222",
"value": "1000000"
}
]
}Errors
All errors return structured JSON:
// 400 Bad Request — unsupported chain
{
"error": "unsupported_chain",
"message": "Chain 'solana' is not supported",
"supported": ["ethereum", "base", "polygon", "arbitrum"]
}
// 404 Not Found — block doesn't exist
{
"detail": "Block 999999999999 not found on ethereum"
}Client Examples
Python
import httpx
api = "http://localhost:8000"
# Get latest Ethereum block
block = httpx.get(f"{api}/chains/ethereum/blocks/latest").json()
print(f"Block {block['number']}: {block['transaction_count']} txs")
# Get full block with token transfers
full = httpx.get(f"{api}/chains/polygon/blocks/50000000/full").json()
for t in full["token_transfers"]:
print(f"{t['token_standard']}: {t['from_address'][:10]}... → {t['to_address'][:10]}...")JavaScript
const api = "http://localhost:8000";
// Get latest block
const block = await fetch(`${api}/chains/ethereum/blocks/latest`).then(r => r.json());
console.log(`Block ${block.number}: ${block.transaction_count} txs`);
// Get transactions
const txs = await fetch(`${api}/chains/ethereum/blocks/18000000/transactions`).then(r => r.json());
txs.forEach(tx => console.log(`${tx.from_address} → ${tx.to_address}: ${tx.value} wei`));curl
# Latest block
curl -s http://localhost:8000/chains/ethereum/blocks/latest | jq
# Full block with token transfers
curl -s http://localhost:8000/chains/arbitrum/blocks/200000000/full | jq '.token_transfers'
# Check response headers
curl -v http://localhost:8000/chains/ethereum 2>&1 | grep "X-"