---
title: "APY & ROI"
method: POST
path: "/api/v2/ethereum/validators/apy-roi"
tags: ["Performance"]
---

# APY & ROI

`POST /api/v2/ethereum/validators/apy-roi`

Returns the annual percentage yield (APY) and return on investment (ROI) for a known collection or group of validators. Known collections are referenced by index, public key, deposit address, or withdrawal address.

**Use case guide:** [APY & ROI metrics](/use-cases/apy-roi) explains how to choose a window, interpret return metrics, and decide when BeaconScore is the better comparison.

For comparing performance across different staking providers, we recommend using the BeaconScore from the Performance Aggregated endpoints instead of APY or ROI, as BeaconScore normalizes for luck and provides a fairer comparison. Note that some residual luck factors outside a validator's control can still influence BeaconScore, especially over short evaluation windows. See the Residual Luck Factors section in the BeaconScore documentation for details.

**Coming Soon:** Support for querying arbitrary time ranges using Unix timestamps, epochs, or slots — exclusively for [Scale and Enterprise plans](https://beaconcha.in/pricing).

## Request body

- ValidatorChainEvaluationWindow
  - `chain` 'mainnet' | 'hoodi' — The Ethereum chain to query.
  - `validator` union, required — Free selectors available to all users: - validator_identifiers: One or more validator indices or public keys to filter by. - dashboard_id: Your beaconcha.in dashboard ID (requires a free account). **Premium selectors** for Scale & Enterprise plans (https://beaconcha.in/pricing): - withdrawal: The validator's withdrawal credential or the Ethereum wallet address used for withdrawals. - deposit_address: The Ethereum wallet address used for the validator's deposit. - entity: The name of the assigned entity (e.g., "Lido", "Coinbase"). Optionally include `sub_entity` for more specific filtering. Matching is case-sensitive. Note: The set of validators matched by `deposit_address` and `withdrawal` selectors is updated once per epoch (~6.4 minutes). Newly deposited validators may take up to one epoch to appear in query results. Note: The set of validators matched by `entity` selector is updated once per day.
    - ValidatorsByIdentifiers
      - `validator_identifiers` ValidatorIndexPublicKey[], required — An array containing either validator indices or public keys. Index and public key can be mixed in the same array. Subscribed users (Hobbyist, Business, and Scale tiers) can include up to 100 entries; free trial users and legacy subscription users (Sapphire, Emerald, Diamond) are limited to 20.
        - union
          - integer — Validator Index
          - string — Public key of a validator
    - ValidatorsByDashboard
      - `dashboard_id` integer, required — beaconcha.in dashboard ID. You can find your dashboard ID in the URL of your dashboard page on beaconcha.in (e.g., https://beaconcha.in/dashboard/12345).
      - `group_id` integer, nullable — Optional beaconcha.in dashboard group ID. If no group ID is provided, all validators in the dashboard are considered.
    - ValidatorsByDeposit
      - `deposit_address` string, required — A standard Ethereum address (20-byte hex string with 0x prefix).
    - ValidatorsByWithdrawal
      - `withdrawal` string, required — Either an execution layer address (20-byte hex string with 0x prefix) or a full 32-byte withdrawal credential.
    - ValidatorsByEntity — Select validators by their assigned entity (e.g., staking provider) and optionally a sub-entity. Entity and sub-entity names are matched exactly and are case-sensitive.
      - `entity` string, required — The name of the entity to filter validators by (e.g., "Lido", "Coinbase"). Matching is case-sensitive; use the exact name as returned by the entities overview endpoint.
      - `sub_entity` string — Optional sub-entity name to further filter validators within the entity. Matching is case-sensitive; use the exact name as returned by the sub-entities overview endpoint.
  - `range` TimeRangeSelectorWithWindow, required
    - `evaluation_window` '24h' | '7d' | '30d' | '90d' | 'all_time', required — The evaluation window for aggregating metrics. All windows except `all_time` are **rolling periods**—continuously moving time windows that always end at the current epoch. Rolling windows update every epoch (~6.4 minutes), so the data always reflects the most recent period. For example, `30d` returns rewards from exactly 30 days ago until now, not a fixed calendar month. - `24h`: Last 24 hours (rolling) - `7d`: Last 7 days (rolling) - `30d`: Last 30 days (rolling) - `90d`: Last 90 days (rolling) - `all_time`: Since validator activation (not rolling)

## Response `200`

Successful response.

- ValidatorApyRoiContainer — Response containing apy and roi information of the validators.
  - `data` ValidatorApyRoiData, required — Validator performance metrics, expressed both as ROI (Return on Investment) and APY (Annual Percentage Yield). - ROI represents the actual return over the selected evaluation window, not annualized. - APY is the same return but annualized to show the equivalent yearly rate. ROI helps understand raw performance during the evaluation range, while APY allows comparison against annualized benchmarks. Note that APY is an extrapolation and does not reflect future performance.
    - `execution_layer` RoiApyMetrics, required
      - `roi` RoiApyMetricsRoi, required — Raw performance in the evaluation window (not annualized).
        - `total` number, float, required — Total return during the selected evaluation window for this layer or combined scope, expressed as a percentage.
        - `missed` number, float, required — Return not earned during the selected evaluation window because of missed duties, expressed as a non-positive percentage.
      - `apy` RoiApyMetricsApy, required — Annualized performance extrapolated from the evaluation window.
        - `total` number, float, required — Total annualized return for this layer or combined scope, expressed as a percentage.
        - `missed` number, float, required — Annualized return not earned because of missed duties, expressed as a non-positive percentage.
    - `consensus_layer` RoiApyMetrics, required
      - `roi` RoiApyMetricsRoi, required — Raw performance in the evaluation window (not annualized).
        - `total` number, float, required — Total return during the selected evaluation window for this layer or combined scope, expressed as a percentage.
        - `missed` number, float, required — Return not earned during the selected evaluation window because of missed duties, expressed as a non-positive percentage.
      - `apy` RoiApyMetricsApy, required — Annualized performance extrapolated from the evaluation window.
        - `total` number, float, required — Total annualized return for this layer or combined scope, expressed as a percentage.
        - `missed` number, float, required — Annualized return not earned because of missed duties, expressed as a non-positive percentage.
    - `combined` RoiApyMetrics, required
      - `roi` RoiApyMetricsRoi, required — Raw performance in the evaluation window (not annualized).
        - `total` number, float, required — Total return during the selected evaluation window for this layer or combined scope, expressed as a percentage.
        - `missed` number, float, required — Return not earned during the selected evaluation window because of missed duties, expressed as a non-positive percentage.
      - `apy` RoiApyMetricsApy, required — Annualized performance extrapolated from the evaluation window.
        - `total` number, float, required — Total annualized return for this layer or combined scope, expressed as a percentage.
        - `missed` number, float, required — Annualized return not earned because of missed duties, expressed as a non-positive percentage.
    - `finality` 'finalized', required — Indicates the finality status of the data provided. - Finalized data cannot be changed without slashing at least one-third of all validators, providing strong economic guarantees. - Data marked as not_finalized does not have this guarantee and may still change.
  - `range` ResultRange, required — The range of data covered by the results, specified in slots, epochs, and Unix timestamps.
    - `slot` SlotRange, required
      - `start` integer, required — Slot by number.
      - `end` integer, required — Slot by number.
    - `epoch` EpochRange, required
      - `start` integer, required
      - `end` integer, required
    - `timestamp` TimeRange, required
      - `start` integer, required
      - `end` integer, required

## Other responses

- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `405` — Method Not Allowed
- `429` — Rate Limit Exceeded
- `500` — Internal Server Error
- `default` — An unexpected error response.

---

[API](https://skmtc.dev/beaconcha/apis/external-service-api.md) · [All operations](https://skmtc.dev/beaconcha/apis/external-service-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/beaconcha/external-service-api/revisions/ad26ad970b4e/schema)
