Overview
Missed rewards represent potential earnings your validators could have received if they had performed perfectly. Tracking missed rewards helps you identify issues, optimize performance, and quantify the cost of downtime or misconfigurations.
API Endpoint: This guide uses /api/v2/ethereum/validators/rewards-list for per-epoch missed reward breakdowns.
See Also: The APY & ROI Metrics endpoint also provides missed reward data as part of its return calculations. Use this document for detailed per-epoch analysis; use APY & ROI for aggregated impact on your returns.
Why Track Missed Rewards?
Understanding Missed Rewards
The Rewards API returns detailed breakdowns including missed_reward fields for each duty type:
Field Description total_missedSum of all missed rewards across all duty types attestation.head.missed_rewardLost rewards from incorrect/late head votes attestation.source.missed_rewardLost rewards from incorrect source votes attestation.target.missed_rewardLost rewards from incorrect target votes sync_committee.missed_rewardLost rewards from missed sync committee duties proposal.missed_cl_rewardEstimated CL rewards lost from missed block proposals (see estimation method below) proposal.missed_el_rewardEstimated EL rewards lost from missed block proposals (see estimation method below)
Missed vs Penalty: Missed rewards are potential earnings you didn’t receive. Penalties are actual deductions from your balance. Both reduce your net rewards, but penalties are more severe.
How Missed Proposal Rewards Are Estimated: The actual value of a missed block is impossible to determine since the block was never produced. We estimate it using the median reward from the 32 surrounding blocks — 16 before and 16 after the missed slot [n-16, n+16). This provides a reasonable approximation based on network conditions at the time.
Quick Start: Fetch Missed Rewards
Get missed rewards for a specific epoch:
curl --request POST \
--url https://beaconcha.in/api/v2/ethereum/validators/rewards-list \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data '
{
"chain": "mainnet",
"validator": {
"dashboard_id": 123
},
"epoch": 414000,
"page_size": 100
}
'
Response
{
"data" : [
{
"validator" : {
"index" : 123456 ,
"public_key" : "0xabc..."
},
"total" : "9103000000000" ,
"total_reward" : "9103000000000" ,
"total_penalty" : "0" ,
"total_missed" : "250000000000" ,
"attestation" : {
"total" : "9103000000000" ,
"head" : {
"total" : "2348000000000" ,
"reward" : "2348000000000" ,
"penalty" : "0" ,
"missed_reward" : "0"
},
"source" : {
"total" : "2364000000000" ,
"reward" : "2364000000000" ,
"penalty" : "0" ,
"missed_reward" : "0"
},
"target" : {
"total" : "4391000000000" ,
"reward" : "4391000000000" ,
"penalty" : "0" ,
"missed_reward" : "0"
}
},
"sync_committee" : {
"total" : "0" ,
"reward" : "0" ,
"penalty" : "0" ,
"missed_reward" : "0"
},
"proposal" : {
"total" : "0" ,
"missed_cl_reward" : "0" ,
"missed_el_reward" : "0"
}
}
]
}
Analyze Missed Rewards by Type
Break down missed rewards to identify problem areas:
import requests
from collections import defaultdict
API_KEY = "<YOUR_API_KEY>"
def get_rewards ( dashboard_id : int , epoch : int ):
"""Fetch rewards for a specific epoch."""
response = requests.post(
"https://beaconcha.in/api/v2/ethereum/validators/rewards-list" ,
headers = {
"Authorization" : f "Bearer { API_KEY } " ,
"Content-Type" : "application/json"
},
json = {
"chain" : "mainnet" ,
"validator" : { "dashboard_id" : dashboard_id},
"epoch" : epoch,
"page_size" : 100
}
)
return response.json()
def analyze_missed_rewards ( dashboard_id : int , epoch : int ):
"""Analyze missed rewards by duty type."""
data = get_rewards(dashboard_id, epoch)
totals = defaultdict( int )
validator_issues = []
for reward in data.get( "data" , []):
validator_idx = reward.get( "validator" , {}).get( "index" )
# Track total missed
total_missed = int (reward.get( "total_missed" , 0 ))
totals[ "total" ] += total_missed
# Attestation breakdown
attestation = reward.get( "attestation" , {})
head_missed = int (attestation.get( "head" , {}).get( "missed_reward" , 0 ))
source_missed = int (attestation.get( "source" , {}).get( "missed_reward" , 0 ))
target_missed = int (attestation.get( "target" , {}).get( "missed_reward" , 0 ))
totals[ "attestation_head" ] += head_missed
totals[ "attestation_source" ] += source_missed
totals[ "attestation_target" ] += target_missed
# Sync committee
sync_missed = int (reward.get( "sync_committee" , {}).get( "missed_reward" , 0 ))
totals[ "sync_committee" ] += sync_missed
# Proposals
proposal = reward.get( "proposal" , {})
proposal_cl_missed = int (proposal.get( "missed_cl_reward" , 0 ))
proposal_el_missed = int (proposal.get( "missed_el_reward" , 0 ))
totals[ "proposal_cl" ] += proposal_cl_missed
totals[ "proposal_el" ] += proposal_el_missed
# Track validators with issues
if total_missed > 0 :
validator_issues.append({
"index" : validator_idx,
"missed_wei" : total_missed,
"missed_eth" : total_missed / 1e18
})
return {
"epoch" : epoch,
"totals" : {k: v / 1e18 for k, v in totals.items()},
"totals_wei" : dict (totals),
"validators_with_issues" : sorted (
validator_issues,
key = lambda x : x[ "missed_wei" ],
reverse = True
)
}
# Example usage
result = analyze_missed_rewards( dashboard_id = 123 , epoch = 414000 )
print ( f "Epoch { result[ 'epoch' ] } Missed Rewards Analysis" )
print ( "=" * 50 )
print ( f "Total Missed: { result[ 'totals' ][ 'total' ] :.6f} ETH" )
print ( f " \n By Duty Type:" )
print ( f " Attestation (Head): { result[ 'totals' ][ 'attestation_head' ] :.6f} ETH" )
print ( f " Attestation (Source): { result[ 'totals' ][ 'attestation_source' ] :.6f} ETH" )
print ( f " Attestation (Target): { result[ 'totals' ][ 'attestation_target' ] :.6f} ETH" )
print ( f " Sync Committee: { result[ 'totals' ][ 'sync_committee' ] :.6f} ETH" )
print ( f " Proposal (CL): { result[ 'totals' ][ 'proposal_cl' ] :.6f} ETH" )
print ( f " Proposal (EL): { result[ 'totals' ][ 'proposal_el' ] :.6f} ETH" )
if result[ "validators_with_issues" ]:
print ( f " \n Top 5 Validators with Missed Rewards:" )
for v in result[ "validators_with_issues" ][: 5 ]:
print ( f " Validator { v[ 'index' ] } : { v[ 'missed_eth' ] :.6f} ETH" )
const API_KEY = '<YOUR_API_KEY>' ;
async function getRewards ( dashboardId , epoch ) {
const response = await fetch (
'https://beaconcha.in/api/v2/ethereum/validators/rewards-list' ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
chain: 'mainnet' ,
validator: { dashboard_id: dashboardId },
epoch ,
page_size: 100
})
}
);
return response . json ();
}
async function analyzeMissedRewards ( dashboardId , epoch ) {
const data = await getRewards ( dashboardId , epoch );
const totals = {
total: 0 n ,
attestation_head: 0 n ,
attestation_source: 0 n ,
attestation_target: 0 n ,
sync_committee: 0 n ,
proposal_cl: 0 n ,
proposal_el: 0 n
};
const validatorIssues = [];
for ( const reward of data . data || []) {
const validatorIdx = reward . validator ?. index ;
const totalMissed = BigInt ( reward . total_missed || '0' );
totals . total += totalMissed ;
// Attestation breakdown
const attestation = reward . attestation || {};
totals . attestation_head += BigInt ( attestation . head ?. missed_reward || '0' );
totals . attestation_source += BigInt ( attestation . source ?. missed_reward || '0' );
totals . attestation_target += BigInt ( attestation . target ?. missed_reward || '0' );
// Sync committee
totals . sync_committee += BigInt ( reward . sync_committee ?. missed_reward || '0' );
// Proposals
const proposal = reward . proposal || {};
totals . proposal_cl += BigInt ( proposal . missed_cl_reward || '0' );
totals . proposal_el += BigInt ( proposal . missed_el_reward || '0' );
if ( totalMissed > 0 n ) {
validatorIssues . push ({
index: validatorIdx ,
missedWei: totalMissed ,
missedEth: Number ( totalMissed ) / 1e18
});
}
}
return {
epoch ,
totals: Object . fromEntries (
Object . entries ( totals ). map (([ k , v ]) => [ k , Number ( v ) / 1e18 ])
),
validatorsWithIssues: validatorIssues . sort (( a , b ) =>
Number ( b . missedWei - a . missedWei )
)
};
}
// Example usage
analyzeMissedRewards ( 123 , 414000 ). then ( result => {
console . log ( `Epoch ${ result . epoch } Missed Rewards Analysis` );
console . log ( '=' . repeat ( 50 ));
console . log ( `Total Missed: ${ result . totals . total . toFixed ( 6 ) } ETH` );
});
Track Missed Rewards Over Time
Monitor trends to detect recurring issues:
def track_missed_rewards_range ( dashboard_id : int , start_epoch : int , end_epoch : int ):
"""Track missed rewards across multiple epochs."""
daily_missed = []
for epoch in range (start_epoch, end_epoch + 1 ):
result = analyze_missed_rewards(dashboard_id, epoch)
daily_missed.append({
"epoch" : epoch,
"total_missed_eth" : result[ "totals" ][ "total" ],
"validators_affected" : len (result[ "validators_with_issues" ])
})
# Print progress every 100 epochs
if epoch % 100 == 0 :
print ( f "Processed epoch { epoch } " )
# Summary
total_missed = sum (d[ "total_missed_eth" ] for d in daily_missed)
avg_per_epoch = total_missed / len (daily_missed) if daily_missed else 0
print ( f " \n Summary: Epochs { start_epoch } to { end_epoch } " )
print ( f "Total Missed: { total_missed :.6f} ETH" )
print ( f "Average per Epoch: { avg_per_epoch :.8f} ETH" )
# Find worst epochs
worst = sorted (daily_missed, key = lambda x : x[ "total_missed_eth" ], reverse = True )[: 5 ]
print ( " \n Worst 5 Epochs:" )
for w in worst:
print ( f " Epoch { w[ 'epoch' ] } : { w[ 'total_missed_eth' ] :.6f} ETH "
f "( { w[ 'validators_affected' ] } validators)" )
return daily_missed
Common Causes of Missed Rewards
Cause Affected Duties Solution Network latency Head votes Reduce latency to beacon node, use local execution client Node downtime All duties Implement redundancy, monitoring, and failover Missed proposals Proposals Ensure MEV-boost and execution client are responsive Sync committee offline Sync committee Keep validators online during sync committee periods Clock drift Attestations Use NTP synchronization
Use the Notifications system to get alerts when validators miss duties. Early detection minimizes losses.
Compare Before/After Changes
Quantify the impact of infrastructure improvements:
def compare_periods ( dashboard_id : int , before_epochs : tuple , after_epochs : tuple ):
"""Compare missed rewards before and after a change."""
def avg_missed ( start , end ):
total = 0
count = 0
for epoch in range (start, end + 1 ):
result = analyze_missed_rewards(dashboard_id, epoch)
total += result[ "totals" ][ "total" ]
count += 1
return total / count if count else 0
before_avg = avg_missed( * before_epochs)
after_avg = avg_missed( * after_epochs)
improvement = ((before_avg - after_avg) / before_avg * 100 ) if before_avg else 0
print ( f "Before: { before_avg :.8f} ETH/epoch" )
print ( f "After: { after_avg :.8f} ETH/epoch" )
print ( f "Improvement: { improvement :.1f} %" )
return {
"before_avg" : before_avg,
"after_avg" : after_avg,
"improvement_pct" : improvement
}
Best Practices
Monitor Regularly Track missed rewards weekly to catch degradation before it becomes significant.
Set Thresholds Alert when missed rewards exceed a percentage of total rewards (e.g., >0.1%).
Compare to BeaconScore Use BeaconScore for relative comparison; use missed rewards for absolute ETH impact.
Investigate Spikes When missed rewards spike, check node logs, network status, and duty assignments.
See Missed Rewards Impact on APY & ROI
For a high-level view of how missed rewards affect your overall returns, use the APY & ROI API . This endpoint provides aggregated missed reward data as part of its return calculations:
curl --request POST \
--url https://beaconcha.in/api/v2/ethereum/validators/apy-roi \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"chain": "mainnet",
"validator": { "dashboard_id": 123 },
"evaluation_window": "30d"
}'
The response includes roi.missed fields showing the impact of missed rewards on your overall ROI:
{
"data" : {
"execution_layer" : {
"roi" : { "total" : 0.0012 , "missed" : -0.0001 },
"apy" : { "total" : 0.0146 , "missed" : -0.0012 }
},
"consensus_layer" : {
"roi" : { "total" : 0.0008 , "missed" : -0.00005 },
"apy" : { "total" : 0.0097 , "missed" : -0.0006 }
}
}
}
When to use which endpoint:
Use rewards-list (this document) for detailed per-epoch missed reward breakdowns by duty type
Use apy-roi for aggregated impact on your overall returns as a percentage