Complete REST API reference for the ChaintracksService endpoints.
http://localhost:3011
Or with routing prefix:
http://localhost:3011/api/v1
All endpoints return JSON with a consistent format.
{
"status": "success",
"value": <result>
}{
"status": "error",
"code": "ERR_CODE",
"description": "Error message"
}All endpoints support CORS with the following headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Access-Control-Expose-Headers: *
Access-Control-Allow-Private-Network: true
Returns server information page.
Response:
Content-Type: text/plain
Chaintracks mainNet Block Header Service
Example:
curl http://localhost:3011/Returns robots exclusion standard file.
Response:
User-agent: *
Disallow: /
Returns the blockchain network the service is tracking.
Response:
{
"status": "success",
"value": "main"
}Values:
"main"- Bitcoin SV mainnet"test"- Bitcoin SV testnet
Example:
curl http://localhost:3011/getChainResponse Example:
{
"status": "success",
"value": "main"
}Returns detailed information about the service state, configuration, and current blockchain heights.
Query Parameters:
wait(optional): Milliseconds to wait before responding (for testing)
Response:
{
"status": "success",
"value": {
"chain": "main",
"heightBulk": 869999,
"heightLive": 870125,
"storage": "ChaintracksStorageNoDb",
"bulkIngestors": [
"BulkIngestorCDNBabbage",
"BulkIngestorWhatsOnChainCdn"
],
"liveIngestors": [
"LiveIngestorWhatsOnChainPoll"
],
"packages": []
}
}Fields:
chain: Network name ('main' or 'test')heightBulk: Highest height in bulk storage (CDN-backed)heightLive: Highest height in live storage (in-memory)storage: Storage backend class namebulkIngestors: List of bulk ingestor class namesliveIngestors: List of live ingestor class namespackages: Package version information (optional)
Example:
curl http://localhost:3011/getInfoNotes:
- Response is never cached (Cache-Control: no-cache)
- Use this endpoint for health checks and monitoring
heightBulkshould be close toheightLive(within ~2000 blocks)
Returns the latest blockchain height from configured bulk ingestors. This represents the current "real" blockchain height from external sources.
Response:
{
"status": "success",
"value": 870125
}Example:
curl http://localhost:3011/getPresentHeightNotes:
- Response is cached for 1 minute
- Value is fetched from WhatsOnChain or other bulk ingestors
- Response is never cached (Cache-Control: no-cache)
Returns the block hash of the active chain tip.
Response:
{
"status": "success",
"value": "00000000000000000123456789abcdef..."
}Example:
curl http://localhost:3011/findChainTipHashHexNotes:
- Response is never cached (Cache-Control: no-cache)
- Returns empty string if no headers available
Returns the complete block header of the active chain tip.
Response:
{
"status": "success",
"value": {
"version": 536870912,
"previousHash": "000000000000000003a1b48cf612e8...",
"merkleRoot": "7c5f9c5e8b8a5c3d2e1f0a9b8c7d6e...",
"time": 1703001234,
"bits": 403123456,
"nonce": 2876543210,
"height": 870125,
"hash": "00000000000000000123456789abcd..."
}
}Fields:
version: Block version numberpreviousHash: Hash of previous block (hex string)merkleRoot: Merkle root of transactions (hex string)time: Block timestamp (Unix epoch seconds)bits: Difficulty target (compact format)nonce: Block nonceheight: Block heighthash: Block hash (hex string)
Example:
curl http://localhost:3011/findChainTipHeaderHexNotes:
- Response is never cached (Cache-Control: no-cache)
- All hash fields are hex strings (lowercase)
Returns the block header for a specific height on the active chain.
Query Parameters:
height(required): Block height (integer)
Response:
{
"status": "success",
"value": {
"version": 536870912,
"previousHash": "000000000000000003a1b48cf612e8...",
"merkleRoot": "7c5f9c5e8b8a5c3d2e1f0a9b8c7d6e...",
"time": 1703001234,
"bits": 403123456,
"nonce": 2876543210,
"height": 800000,
"hash": "00000000000000000123456789abcd..."
}
}If height not found:
{
"status": "success",
"value": null
}Example:
curl "http://localhost:3011/findHeaderHexForHeight?height=800000"Notes:
- Returns
nullif height doesn't exist - Only returns headers on active chain
- Fast O(1) lookup
Returns the block header for a specific block hash (if in live storage).
Query Parameters:
hash(required): Block hash (hex string)
Response:
{
"status": "success",
"value": {
"version": 536870912,
"previousHash": "000000000000000003a1b48cf612e8...",
"merkleRoot": "7c5f9c5e8b8a5c3d2e1f0a9b8c7d6e...",
"time": 1703001234,
"bits": 403123456,
"nonce": 2876543210,
"height": 870125,
"hash": "00000000000000000123456789abcd..."
}
}If hash not found:
{
"status": "success",
"value": null
}Example:
curl "http://localhost:3011/findHeaderHexForBlockHash?hash=00000000000000000123456789abcd..."Notes:
- Only searches live storage (recent ~2000 blocks)
- Returns
nullif hash not found or in bulk storage - For older headers, use
findHeaderHexForHeightinstead
Returns multiple block headers in serialized format starting from a specific height.
Query Parameters:
height(required): Starting block height (integer)count(required): Number of headers to return (integer, max recommended: 1000)
Response:
{
"status": "success",
"value": "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c..."
}Format:
- Returns hex string of concatenated 80-byte block headers
- Each header is 80 bytes (160 hex characters)
- Total length:
count × 160characters - Headers are in order from
heighttoheight + count - 1
Example:
# Get 10 headers starting from height 800000
curl "http://localhost:3011/getHeaders?height=800000&count=10"Parsing the Response:
const response = await fetch('http://localhost:3011/getHeaders?height=800000&count=10')
const data = await response.json()
const headersHex = data.value
// Each header is 160 hex chars (80 bytes)
const headerSize = 160
const count = headersHex.length / headerSize
for (let i = 0; i < count; i++) {
const start = i * headerSize
const headerHex = headersHex.substring(start, start + headerSize)
console.log(`Header ${800000 + i}: ${headerHex}`)
}Notes:
- Efficient for bulk header downloads
- Use for SPV client synchronization
- Recommended to request in batches (e.g., 100-1000 headers)
Returns current fiat exchange rates for BSV from configured services.
Response:
{
"status": "success",
"value": {
"timestamp": 1703001234000,
"base": "BSV",
"rates": {
"USD": 45.23,
"EUR": 41.87,
"GBP": 35.92,
"JPY": 6234.56
}
}
}Example:
curl http://localhost:3011/getFiatExchangeRatesNotes:
- Response is never cached (Cache-Control: no-cache)
- Rates are fetched from external services
- May return empty object if services unavailable
Submits a new block header for consideration and processing.
Request Body:
{
"version": 536870912,
"previousHash": "000000000000000003a1b48cf612e8...",
"merkleRoot": "7c5f9c5e8b8a5c3d2e1f0a9b8c7d6e...",
"time": 1703001234,
"bits": 403123456,
"nonce": 2876543210
}Response:
{
"status": "success"
}Fields:
version: Block version number (integer)previousHash: Hash of previous block (hex string, 64 chars)merkleRoot: Merkle root (hex string, 64 chars)time: Block timestamp (Unix epoch seconds, integer)bits: Difficulty target in compact format (integer)nonce: Block nonce (integer)
Example:
curl -X POST http://localhost:3011/addHeaderHex \
-H "Content-Type: application/json" \
-d '{
"version": 536870912,
"previousHash": "000000000000000003a1b48cf612e8...",
"merkleRoot": "7c5f9c5e8b8a5c3d2e1f0a9b8c7d6e...",
"time": 1703001234,
"bits": 403123456,
"nonce": 2876543210
}'Processing:
- Header is queued for processing (returns immediately)
- Header is validated and inserted asynchronously
- If previous header is unknown, header is ignored
- Invalid headers are rejected silently
Notes:
- Response does not indicate if header was accepted/added
- Use for submitting newly mined blocks
- Header must have valid proof-of-work
- Previous header must already exist in storage
Generic internal server error.
Example:
{
"status": "error",
"code": "ERR_INTERNAL",
"description": "An internal error has occurred."
}Common Causes:
- Storage operation failed
- Invalid data format
- Unhandled exception
The server does not implement rate limiting by default. For production use, consider:
- Reverse proxy with rate limiting (nginx, caddy)
- API gateway with rate limiting
- Application-level rate limiting middleware
Most endpoints include cache headers:
No Cache (dynamic data):
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Applies to:
/getInfo/getPresentHeight/findChainTipHashHex/findChainTipHeaderHex/getFiatExchangeRates
Cacheable (static data):
No explicit cache headers. Clients may cache based on:
- Block height (immutable once confirmed)
- Block hash (immutable)
Applies to:
/findHeaderHexForHeight?height=N(for heights < chain tip - 100)/findHeaderHexForBlockHash?hash=H(for deep blocks)/getHeaders?height=N&count=M(for heights < chain tip - 100)
The service implements internal caching:
getPresentHeight(): Cached for 1 minute- Block headers: Cached in memory (NoDb storage)
- Bulk files: Cached in memory (limited by
maxRetained)
The service does not currently support WebSocket connections. For real-time updates:
- Poll
/getInfoendpoint (recommended interval: 30-60 seconds) - Subscribe to events programmatically if using the service as a library
- Implement custom WebSocket wrapper on top of the service
curl http://localhost:3011/getInfoCheck that:
- Response status is 200
statusfield is "success"heightLiveis increasing over time
# Get present height (external blockchain height)
PRESENT=$(curl -s http://localhost:3011/getPresentHeight | jq -r .value)
# Get service height (internal height)
SERVICE=$(curl -s http://localhost:3011/getInfo | jq -r .value.heightLive)
# Calculate lag
LAG=$((PRESENT - SERVICE))
echo "Lag: $LAG blocks"
# Alert if lag > 10 blocks
if [ $LAG -gt 10 ]; then
echo "WARNING: Service is lagging behind blockchain"
fiFor downloading many headers:
// Good: Request in batches
const batchSize = 1000
for (let height = 0; height < targetHeight; height += batchSize) {
const headers = await getHeaders(height, batchSize)
processHeaders(headers)
}
// Bad: Request one at a time
for (let height = 0; height < targetHeight; height++) {
const header = await findHeaderForHeight(height)
processHeader(header)
}If you need chain tip frequently:
// Cache chain tip for 10 seconds
let cachedTip = null
let cacheTime = 0
async function getChainTip() {
const now = Date.now()
if (!cachedTip || now - cacheTime > 10000) {
cachedTip = await fetch('http://localhost:3011/findChainTipHeaderHex')
.then(r => r.json())
.then(d => d.value)
cacheTime = now
}
return cachedTip
}The service handles concurrent requests well:
// Good: Request in parallel
const [chainTip, height, info] = await Promise.all([
getChainTipHeader(),
getPresentHeight(),
getInfo()
])
// Bad: Request sequentially
const chainTip = await getChainTipHeader()
const height = await getPresentHeight()
const info = await getInfo()See src/client-example.ts for a complete TypeScript client implementation with all endpoints.
# Get service info
curl http://localhost:3011/getInfo | jq
# Get current height
curl http://localhost:3011/getPresentHeight | jq
# Get chain tip
curl http://localhost:3011/findChainTipHeaderHex | jq
# Get header by height
curl "http://localhost:3011/findHeaderHexForHeight?height=800000" | jq
# Get 10 headers
curl "http://localhost:3011/getHeaders?height=800000&count=10" | jq -r .value
# Submit new header
curl -X POST http://localhost:3011/addHeaderHex \
-H "Content-Type: application/json" \
-d @new-header.json// Using fetch
const response = await fetch('http://localhost:3011/getInfo')
const data = await response.json()
console.log('Height:', data.value.heightLive)
// Using axios
const axios = require('axios')
const { data } = await axios.get('http://localhost:3011/getPresentHeight')
console.log('Height:', data.value)import requests
# Get service info
response = requests.get('http://localhost:3011/getInfo')
data = response.json()
print(f"Height: {data['value']['heightLive']}")
# Get header by height
response = requests.get('http://localhost:3011/findHeaderHexForHeight',
params={'height': 800000})
header = response.json()['value']
print(f"Hash: {header['hash']}")- Check server is running:
curl http://localhost:3011/ - Verify port: Check
PORTenvironment variable - Check firewall settings
- Verify endpoint path is correct
- Check routing prefix configuration
- Ensure server has finished starting
- Check server logs for errors
- Verify storage is initialized
- Check bulk ingestors are accessible
- Service may still be synchronizing
- Check
/getInfofor current state - Wait for
heightLiveto increase
- Service may be synchronizing
- Check network connectivity to CDN/WhatsOnChain
- Consider increasing
maxRetainedfor better caching