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/full

Response Headers

Every response includes:

HeaderExampleDescription
X-Request-Ida1b2c3d4Unique request identifier for debugging
X-Response-Time42.5msServer-side processing time
X-Powered-ByChainETLService 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/chains

List 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

chainstringChain name (ethereum, base, polygon, arbitrum)

Response

{
  "chain": "ethereum",
  "latest_block": 19234567,
  "rpc_url": "https://eth.llamarpc.com"
}
GET/chains/{chain}/blocks/latest

Get the most recent block

Parameters

chainstringChain 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

chainstringChain name
block_numberintegerBlock number

Response

{
  "chain": "ethereum",
  "number": 18000000,
  "hash": "0xabc...def",
  "parent_hash": "0x123...456",
  "timestamp": 1693526400,
  "transaction_count": 98
}
GET/chains/{chain}/blocks/{block_number}/transactions

Get all transactions in a block

Parameters

chainstringChain name
block_numberintegerBlock 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}/full

Get a block with all transactions, logs, and token transfers

Parameters

chainstringChain name
block_numberintegerBlock 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-"