Skip to main content

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?

Diagnose Issues

Identify which duty types (attestations, proposals, sync) are causing losses.

Quantify Downtime Cost

Calculate the exact ETH lost during outages or maintenance windows.

Optimize Setup

Compare missed rewards before/after configuration changes to measure improvements.

Client Reporting

Show customers transparent breakdowns of actual vs. potential earnings.

Impact on Returns

See how missed rewards affect your overall APY & ROI using aggregated metrics.

Understanding Missed Rewards

The Rewards API returns detailed breakdowns including missed_reward fields for each duty type:
FieldDescription
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"\nBy 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"\nTop 5 Validators with Missed Rewards:")
    for v in result["validators_with_issues"][:5]:
        print(f"  Validator {v['index']}: {v['missed_eth']:.6f} 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"\nSummary: 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("\nWorst 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

CauseAffected DutiesSolution
Network latencyHead votesReduce latency to beacon node, use local execution client
Node downtimeAll dutiesImplement redundancy, monitoring, and failover
Missed proposalsProposalsEnsure MEV-boost and execution client are responsive
Sync committee offlineSync committeeKeep validators online during sync committee periods
Clock driftAttestationsUse 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