Documentation Index Fetch the complete documentation index at: https://docs.octav.fi/llms.txt
Use this file to discover all available pages before exploring further.
Query Ethereum beacon chain data for individual validators by index or BLS pubkey. All endpoints are read-only and source data from a self-hosted goteth ClickHouse instance via Octav’s beacon-svc (mainnet only).
Beacon validator endpoints are an add-on , billed at a set monthly fee separate from your API credit pool. Contact sales to enable beacon access for your account.
Cost: Set monthly fee under the beacon add-on — no per-call credit deduction.
Rate limit: 360 requests/min, shared with your portfolio bucket.
Path parameters
All endpoints accept one of two path-parameter forms:
Parameter Type Description :indexinteger Non-negative validator index :pubkeystring 48-byte BLS pubkey, hex-encoded with 0x prefix (96 hex characters). Case-insensitive
Note: BLS pubkeys are validator identifiers , not wallet addresses.
Validator Details
Get current state, lifecycle epochs, effective balance, and withdrawal credentials for a single validator.
Endpoints
GET https://api.octav.fi/v1/beacon/validators/details/index/:index
GET https://api.octav.fi/v1/beacon/validators/details/pubkey/:pubkey
Response
Core validator identity and status. BLS pubkey (lowercase 0x-prefixed)
Whether the validator has been slashed
Validator lifecycle status. One of: pending | active_ongoing | active_exiting | active_slashed | exited_unslashed | exited_slashed | withdrawal_possible | withdrawal_done
Epoch-level lifecycle milestones. Fields are null when not yet reached. lifecycle.activationEpoch
Epoch when the validator became active
lifecycle.activationTimestamp
Unix timestamp of activation
Epoch when the validator exited (or will exit)
lifecycle.withdrawableEpoch
Epoch when withdrawal becomes possible
Validator balances as wei strings. Effective balance in wei (used for reward/penalty calculations)
Withdrawal credential info. address is only present for 0x01-prefix validators. withdrawalCredentials.prefix
Credential prefix byte (0 = BLS, 1 = EVM address)
withdrawalCredentials.credential
Full credential hex string
withdrawalCredentials.address
EVM withdrawal address (0x01 only, otherwise null)
Example
curl "https://api.octav.fi/v1/beacon/validators/details/index/123456" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"validator" : {
"index" : 123456 ,
"pubkey" : "0xb03d97937ae39b38a44f90141dcf7f0420c3b9b21caee1a7603046bc9237f5c96b61714474249dafe0a20af583a4fc07" ,
"slashed" : false ,
"status" : "active_ongoing"
},
"lifecycle" : {
"activationEpoch" : 34567 ,
"activationTimestamp" : 1672531200 ,
"exitEpoch" : null ,
"withdrawableEpoch" : null
},
"balance" : {
"actual" : "32045123456789" ,
"effective" : "32000000000000"
},
"withdrawalCredentials" : {
"prefix" : 1 ,
"credential" : "0x0100000000000000000000000a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2" ,
"address" : "0x0a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2"
}
}
Validator Rewards
Paginated reward buckets aggregated by epoch, day, week, or month for a time range. Typical use case: pull daily rewards for a known validator pubkey for portfolio reporting (Lido, Beaver, etc.).
Data availability: Rewards are available from the Merge (epoch 146875, September 15, 2022) onward. If you need data going further back, contact us.
Endpoints
GET https://api.octav.fi/v1/beacon/validators/rewards/index/:index
GET https://api.octav.fi/v1/beacon/validators/rewards/pubkey/:pubkey
Query Parameters
Determines how rangeFrom / rangeTo are interpreted.
timestamp — values are Unix seconds. Max range: 31,536,000 seconds (1 year).
epoch — values are beacon epoch numbers. Max range: 82,125 epochs (1 year).
Start of the range (inclusive). Must be ≥ 0 and ≤ rangeTo. Interpreted as Unix seconds or epoch number per rangeType.
End of the range (inclusive). Must be ≥ 0. Interpreted as Unix seconds or epoch number per rangeType.
Maximum 1-year range. Cap is 31,536,000 seconds when rangeType=timestamp, 82,125 epochs when rangeType=epoch. For better performance, keep ranges as small as your use case allows.
Bucket size for aggregation. One of: epoch | day | week | month
Pagination offset (number of records to skip).
Number of records to return. Max 10.
Response
Array of reward buckets. Show ValidatorRewardSummary fields
First epoch in this bucket
Last epoch in this bucket
Unix timestamp of bucket start
Unix timestamp of bucket end
ISO date string of bucket start
ISO date string of bucket end
Consensus layer reward in wei
Maximum possible consensus reward in wei
Ratio of actual to potential consensus reward (0–1), or null if not computable
Execution layer (MEV + tips) reward in wei
Number of attestations included on-chain
Number of attestations missed
Sync committee participations
Sync committee slots missed
Effective balance at the end of the bucket, in wei
Current pagination offset
Total number of matching buckets
Example
cURL (timestamp mode)
cURL (epoch mode)
JavaScript
Python
curl "https://api.octav.fi/v1/beacon/validators/rewards/index/123456?rangeType=timestamp&rangeFrom=1704067200&rangeTo=1706745600&granularity=day&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data" : [
{
"epochStart" : 231200 ,
"epochEnd" : 231424 ,
"timestampStart" : 1704067200 ,
"timestampEnd" : 1704153600 ,
"dateStart" : "2024-01-01" ,
"dateEnd" : "2024-01-02" ,
"consensusReward" : "2450000000000" ,
"consensusPotentialReward" : "2500000000000" ,
"consensusEfficiency" : 0.98 ,
"executionReward" : "125000000000" ,
"attestationsIncluded" : 224 ,
"attestationsMissed" : 0 ,
"syncParticipations" : 0 ,
"syncMissed" : 0 ,
"proposedBlocks" : 0 ,
"missedBlocks" : 0 ,
"mevBlocks" : 0 ,
"endEffectiveBalance" : "32000000000000"
}
],
"offset" : 0 ,
"limit" : 10 ,
"totalRows" : 31 ,
"pages" : 4
}
Validator Withdrawals
Paginated list of withdrawals processed for a validator.
Endpoints
GET https://api.octav.fi/v1/beacon/validators/withdrawals/index/:index
GET https://api.octav.fi/v1/beacon/validators/withdrawals/pubkey/:pubkey
Query Parameters
Number of records to return. Max 10.
Response
Array of withdrawal events. Show ValidatorWithdrawal fields
Slot in which the withdrawal was processed
Epoch containing the slot
Unix timestamp of the withdrawal
EVM address that received the withdrawal
Current pagination offset
Total number of withdrawals
Example
curl "https://api.octav.fi/v1/beacon/validators/withdrawals/index/123456?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data" : [
{
"slot" : 8901234 ,
"epoch" : 278163 ,
"timestamp" : 1706745600 ,
"amount" : "32000000000000" ,
"address" : "0x0a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2" ,
"index" : 123456
}
],
"offset" : 0 ,
"limit" : 10 ,
"totalRows" : 3 ,
"pages" : 1
}
Validator Deposits
Paginated list of deposits made to a validator.
Endpoints
GET https://api.octav.fi/v1/beacon/validators/deposits/index/:index
GET https://api.octav.fi/v1/beacon/validators/deposits/pubkey/:pubkey
Query Parameters
Number of records to return. Max 10.
Response
Array of deposit events. Show ValidatorDeposit fields
Slot in which the deposit was included
Epoch containing the slot
Unix timestamp of the deposit
Global deposit contract index
Full withdrawal credential hex string
EVM withdrawal address (0x01 prefix only, otherwise null)
Current pagination offset
Example
curl "https://api.octav.fi/v1/beacon/validators/deposits/pubkey/0xb03d97937ae39b38a44f90141dcf7f0420c3b9b21caee1a7603046bc9237f5c96b61714474249dafe0a20af583a4fc07?limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data" : [
{
"slot" : 1234567 ,
"epoch" : 38579 ,
"timestamp" : 1634567890 ,
"amount" : "32000000000000000000" ,
"depositIndex" : 456789 ,
"withdrawalCredentials" : "0x0100000000000000000000000a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2" ,
"withdrawalAddress" : "0x0a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2"
}
],
"offset" : 0 ,
"limit" : 10 ,
"totalRows" : 1 ,
"pages" : 1
}
Error Responses
Input validation failed (Joi structured error). { "error" : " \" index \" must be a non-negative integer" }
Common causes: invalid :index or :pubkey format, missing required query params, rangeFrom > rangeTo, range exceeding 1-year cap, invalid rangeType or granularity value.
Missing or invalid Bearer token. { "message" : "Unauthorized" }
Valid token but account does not have enterprise beacon access. { "error" : "Beacon access requires enterprise subscription. Contact sales." }
Validator does not exist on mainnet. { "error" : "Validator not found for index 99999999" }
500 Internal Server Error
Upstream beacon data error. Error fetching validator details
Use Cases
Aggregate daily rewards across a set of validators and convert wei to ETH for portfolio reporting. async function getDailyStakingYield ( pubkeys , fromTs , toTs ) {
const buckets = {};
for ( const pubkey of pubkeys ) {
const params = new URLSearchParams ({
rangeType: 'timestamp' ,
rangeFrom: String ( fromTs ),
rangeTo: String ( toTs ),
granularity: 'day' ,
limit: '10'
});
const response = await fetch (
`https://api.octav.fi/v1/beacon/validators/rewards/pubkey/ ${ pubkey } ? ${ params } ` ,
{ headers: { 'Authorization' : `Bearer ${ apiKey } ` } }
);
const { data } = await response . json ();
data . forEach ( b => {
const total = BigInt ( b . consensusReward ) + BigInt ( b . executionReward );
buckets [ b . dateStart ] = ( buckets [ b . dateStart ] ?? 0 n ) + total ;
});
}
return Object . entries ( buckets ). map (([ date , wei ]) => ({
date ,
eth: Number ( wei ) / 1e18
}));
}
Read the canonical validator.status value to drive lifecycle UI or alerting. export const ValidatorStatus = {
Pending: 'pending' ,
ActiveOngoing: 'active_ongoing' ,
ActiveExiting: 'active_exiting' ,
ActiveSlashed: 'active_slashed' ,
ExitedUnslashed: 'exited_unslashed' ,
ExitedSlashed: 'exited_slashed' ,
WithdrawalPossible: 'withdrawal_possible' ,
WithdrawalDone: 'withdrawal_done' ,
} as const ;
async function getValidatorStatus ( index ) {
const response = await fetch (
`https://api.octav.fi/v1/beacon/validators/details/index/ ${ index } ` ,
{ headers: { 'Authorization' : `Bearer ${ apiKey } ` } }
);
const { validator } = await response . json ();
if ( validator . status === ValidatorStatus . ActiveSlashed ||
validator . status === ValidatorStatus . ExitedSlashed ) {
alert ( `Validator ${ validator . index } has been slashed` );
}
return validator . status ;
}
Paginate every withdrawal for a validator and bucket totals by recipient address (useful for tax reporting and proof-of-reserves). async function getWithdrawalsByAddress ( index ) {
const totals = {};
let offset = 0 ;
while ( true ) {
const response = await fetch (
`https://api.octav.fi/v1/beacon/validators/withdrawals/index/ ${ index } ?limit=10&offset= ${ offset } ` ,
{ headers: { 'Authorization' : `Bearer ${ apiKey } ` } }
);
const { data , totalRows } = await response . json ();
data . forEach ( w => {
totals [ w . address ] = ( totals [ w . address ] ?? 0 n ) + BigInt ( w . amount );
});
offset += data . length ;
if ( offset >= totalRows || data . length === 0 ) break ;
}
return Object . entries ( totals ). map (([ address , wei ]) => ({
address ,
eth: Number ( wei ) / 1e18
}));
}
Best Practices
Cache Validator Details Client-Side
validator.status, lifecycle.*, and withdrawalCredentials.* change on the order of minutes-to-hours, not seconds. Cache details responses for several minutes to cut latency and load.const detailsCache = new Map (); // index -> { value, expiresAt }
const TTL_MS = 5 * 60 * 1000 ;
async function getValidatorDetails ( index ) {
const cached = detailsCache . get ( index );
if ( cached && cached . expiresAt > Date . now ()) return cached . value ;
const response = await fetch (
`https://api.octav.fi/v1/beacon/validators/details/index/ ${ index } ` ,
{ headers: { 'Authorization' : `Bearer ${ apiKey } ` } }
);
const value = await response . json ();
detailsCache . set ( index , { value , expiresAt: Date . now () + TTL_MS });
return value ;
}
Pick Granularity to Match Range
Reward buckets at granularity=epoch over a one-year range can yield ~82,000 rows. Match the granularity to the range so a single query returns a meaningful page. Range Suggested granularity < 1 day epoch1–30 days day1–6 months week6 months – 1 year month
Going finer than this still works, but you’ll need to paginate (limit is capped at 10) and stitch results together.
Reward and balance fields are returned as decimal strings in wei. Summing across long ranges or many validators can exceed Number.MAX_SAFE_INTEGER — parse with BigInt, divide by 10^18 only at the display step. const totalWei = rewards . data . reduce (
( sum , b ) => sum + BigInt ( b . consensusReward ) + BigInt ( b . executionReward ),
0 n
);
const totalEth = Number ( totalWei ) / 1e18 ;
Paginate to Fetch All Records
limit is capped at 10 for rewards, withdrawals, and deposits. Loop on offset until you’ve consumed totalRows.async function fetchAll ( url ) {
const all = [];
let offset = 0 ;
while ( true ) {
const sep = url . includes ( '?' ) ? '&' : '?' ;
const response = await fetch (
` ${ url }${ sep } limit=10&offset= ${ offset } ` ,
{ headers: { 'Authorization' : `Bearer ${ apiKey } ` } }
);
const { data , totalRows } = await response . json ();
all . push ( ... data );
offset += data . length ;
if ( offset >= totalRows || data . length === 0 ) break ;
}
return all ;
}
Portfolio Get full portfolio holdings for wallet addresses
Transactions View on-chain transaction history