> ## Documentation Index
> Fetch the complete documentation index at: https://docs.beaconcha.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Proposal Timing Aggregate

> Returns when the proposals of a set of validators reached one beaconcha.in node, aggregated over a 24-hour, 7-day or 30-day evaluation window, next to the same figures for the whole network over the same window. The validator set may be selected by identifiers, dashboard, withdrawal address, withdrawal credential, deposit address, or entity/sub-entity.

From the Glamsterdam hard fork on, the proposer publishes its own block, so the time at which a block reaches our node follows the proposer's setup; the winning builder may also forward the block, which can make it arrive earlier. The figures are one node's view: each node receives a block at its own time, and a proposer's network position gives it a stable offset to our node, so a difference between proposers is not by itself a difference in when they published. The time is when our node received the block over the peer-to-peer gossip network, relative to the slot start; a block our node imported another way has no time and is counted separately.

`publish_time.median_slot_offset_vs_network_ms` compares each proposal with the network median of the same hour of slot start, so a slow hour at our node does not count against the validators. The network median includes the validators' own proposals, which pulls the offset of a large set toward zero.

The attestation deadline is `ATTESTATION_DUE_BPS_GLOAS` of the slot duration, 3000 ms at 12-second slots. Fork choice counts a block as timely only when it arrives before the deadline, and each node judges by its own arrival time, so a count at or after the deadline describes our node's view, not the chain's verdict. A block that our node received but the chain did not include is counted in `not_included_count`; the arrival time alone does not show why the chain did not include it.

Only finalized slots count, and the window never reaches before the fork. The window ends at the last slot for which both the included and the missed proposals are recorded, at most the last slot of the epoch before the head; `range` returns the window evaluated. When no slot of the window has a verdict yet, `range` returns the requested window, and in the fork's own epoch, before any slot after the fork can have a verdict, the first slot after the fork. Medians and percentiles are exact, not interpolated: for an even count the median is the upper of the two middle values. A median needs at least 20 proposals seen on gossip and a 90th percentile at least 50; below that the field is null and the counts remain. A set of a few validators rarely reaches the minimum within a window, because one validator proposes only every few months on a large network.

Combine this endpoint with others for deeper insights:
- Each proposal of the validators, with its own publish time: [v2/ethereum/validators/proposal-slots](/api-reference/ethereum/validators/proposal-slots)
- The same figures for an entity or one of its sub-entities: [v2/ethereum/entity/proposal-timing](/api-reference/ethereum/entity/proposal-timing)
- Before the fork, proposal timing measured from relay bids, which is not comparable with this measure: [v2/ethereum/validators/mev-timing-aggregate](/api-reference/ethereum/validators/mev-timing-aggregate)


> [!WARNING]
>
> **Experimental.** This endpoint returns data only on chains past the Glamsterdam hard fork. Hoodi forks first; mainnet returns `404` until it forks. The request and response may change before the mainnet release. See [Glamsterdam API](https://docs.beaconcha.in/api/glamsterdam.md).


## OpenAPI

````yaml /v3/bundled.yaml post /api/v2/ethereum/validators/proposal-timing-aggregate
openapi: 3.0.4
info:
  title: External Service API
  version: 1.0.3
servers:
  - url: https://beaconcha.in
    description: Production API
security:
  - ApiKeyAuth: []
paths:
  /api/v2/ethereum/validators/proposal-timing-aggregate:
    post:
      tags:
        - Validator
      summary: Proposal Timing Aggregate
      description: >
        Returns when the proposals of a set of validators reached one
        beaconcha.in node, aggregated over a 24-hour, 7-day or 30-day evaluation
        window, next to the same figures for the whole network over the same
        window. The validator set may be selected by identifiers, dashboard,
        withdrawal address, withdrawal credential, deposit address, or
        entity/sub-entity.


        From the Glamsterdam hard fork on, the proposer publishes its own block,
        so the time at which a block reaches our node follows the proposer's
        setup; the winning builder may also forward the block, which can make it
        arrive earlier. The figures are one node's view: each node receives a
        block at its own time, and a proposer's network position gives it a
        stable offset to our node, so a difference between proposers is not by
        itself a difference in when they published. The time is when our node
        received the block over the peer-to-peer gossip network, relative to the
        slot start; a block our node imported another way has no time and is
        counted separately.


        `publish_time.median_slot_offset_vs_network_ms` compares each proposal
        with the network median of the same hour of slot start, so a slow hour
        at our node does not count against the validators. The network median
        includes the validators' own proposals, which pulls the offset of a
        large set toward zero.


        The attestation deadline is `ATTESTATION_DUE_BPS_GLOAS` of the slot
        duration, 3000 ms at 12-second slots. Fork choice counts a block as
        timely only when it arrives before the deadline, and each node judges by
        its own arrival time, so a count at or after the deadline describes our
        node's view, not the chain's verdict. A block that our node received but
        the chain did not include is counted in `not_included_count`; the
        arrival time alone does not show why the chain did not include it.


        Only finalized slots count, and the window never reaches before the
        fork. The window ends at the last slot for which both the included and
        the missed proposals are recorded, at most the last slot of the epoch
        before the head; `range` returns the window evaluated. When no slot of
        the window has a verdict yet, `range` returns the requested window, and
        in the fork's own epoch, before any slot after the fork can have a
        verdict, the first slot after the fork. Medians and percentiles are
        exact, not interpolated: for an even count the median is the upper of
        the two middle values. A median needs at least 20 proposals seen on
        gossip and a 90th percentile at least 50; below that the field is null
        and the counts remain. A set of a few validators rarely reaches the
        minimum within a window, because one validator proposes only every few
        months on a large network.


        Combine this endpoint with others for deeper insights:

        - Each proposal of the validators, with its own publish time:
        [v2/ethereum/validators/proposal-slots](/api-reference/ethereum/validators/proposal-slots)

        - The same figures for an entity or one of its sub-entities:
        [v2/ethereum/entity/proposal-timing](/api-reference/ethereum/entity/proposal-timing)

        - Before the fork, proposal timing measured from relay bids, which is
        not comparable with this measure:
        [v2/ethereum/validators/mev-timing-aggregate](/api-reference/ethereum/validators/mev-timing-aggregate)
      operationId: GetValidatorProposalTimingAggregate
      requestBody:
        $ref: '#/components/requestBodies/validatorProposalTimingAggregate'
      responses:
        '200':
          $ref: '#/components/responses/ValidatorProposalTiming'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalServerError'
        default:
          $ref: '#/components/responses/DefaultError'
components:
  requestBodies:
    validatorProposalTimingAggregate:
      x-go-name: ValidatorProposalTimingAggregateRequestBody
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidatorProposalTimingRequest'
          example:
            validator:
              validator_identifiers:
                - 84662
                - 84663
            range:
              evaluation_window: 30d
            chain: hoodi
  responses:
    ValidatorProposalTiming:
      description: Successful response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidatorProposalTiming.Container'
    BadRequest:
      description: Bad Request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Bad request. Please check your input and try again.
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Unauthorized. Please provide a valid API key.
    Forbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: >-
              endpoint not allowed for your subscription tier. upgrade your
              subscription at https://beaconcha.in/pricing.
    NotFound:
      description: Not Found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: The requested resource was not found.
    MethodNotAllowed:
      description: Method Not Allowed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: 'method not allowed: GET. all public API endpoints use POST.'
    RateLimitExceeded:
      description: Rate Limit Exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Rate limit exceeded. Please try again later.
    InternalServerError:
      description: Internal Server Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: internal server error. please try again later.
    DefaultError:
      description: An unexpected error response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: An unexpected error occurred.
  schemas:
    ValidatorProposalTimingRequest:
      type: object
      properties:
        chain:
          $ref: '#/components/schemas/Chain'
        validator:
          $ref: '#/components/schemas/validatorsSelector'
        range:
          $ref: '#/components/schemas/NamedProposalTimingEvaluationWindow'
      required:
        - validator
        - range
    ValidatorProposalTiming.Container:
      type: object
      description: >-
        Response containing when the proposals of the validators reached one
        beaconcha.in node over the evaluation window.
      properties:
        data:
          $ref: '#/components/schemas/ValidatorProposalTiming.Data'
        range:
          $ref: '#/components/schemas/ResultRange'
      required:
        - data
        - range
    Error:
      type: object
      properties:
        error:
          type: string
    Chain:
      type: string
      enum:
        - mainnet
        - hoodi
      default: mainnet
      description: The Ethereum chain to query.
    validatorsSelector:
      description: >-
        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.
          
      oneOf:
        - $ref: '#/components/schemas/ValidatorsByIdentifiers'
        - $ref: '#/components/schemas/ValidatorsByDashboard'
        - $ref: '#/components/schemas/ValidatorsByDeposit'
        - $ref: '#/components/schemas/ValidatorsByWithdrawal'
        - $ref: '#/components/schemas/ValidatorsByEntity'
    NamedProposalTimingEvaluationWindow:
      type: object
      title: Fixed Window
      properties:
        evaluation_window:
          $ref: '#/components/schemas/ProposalTimingEvaluationWindow'
      required:
        - evaluation_window
    ValidatorProposalTiming.Data:
      type: object
      description: >-
        Proposal timing of the validators over the evaluation window. The window
        ends at the last finalized slot and starts no earlier than the
        Glamsterdam hard fork.
      properties:
        evaluation_window:
          $ref: '#/components/schemas/ProposalTimingEvaluationWindow'
        publish_time:
          $ref: '#/components/schemas/ValidatorProposalTiming.PublishTime'
      required:
        - evaluation_window
        - publish_time
    ResultRange:
      type: object
      description: >
        The time span actually covered by the returned results — from the first
        to the last matching data point — specified in slots, epochs, and Unix
        timestamps.


        This reflects the data that was found, not the range that was queried.
        When the response is `200` but no data is found, it falls back to the
        requested range, or to the full available history if no range was given.
        Some endpoints answer `404` instead when no data is found.


        For arbitrary (custom) time-range queries the queried range is expanded
        outward to whole UTC days, so the covered range may be wider than
        requested. As stated above, the reported range still reflects the data
        that was actually found — a touched UTC day with no data for the
        selected validators will not extend it.
      properties:
        slot:
          $ref: '#/components/schemas/SlotRange'
        epoch:
          $ref: '#/components/schemas/EpochRange'
        timestamp:
          $ref: '#/components/schemas/TimeRange'
      required:
        - slot
        - epoch
        - timestamp
    ValidatorsByIdentifiers:
      type: object
      title: Indices/Pubkeys
      properties:
        validator_identifiers:
          $ref: '#/components/schemas/validatorIndexPublicKeys'
      required:
        - validator_identifiers
    ValidatorsByDashboard:
      type: object
      title: Dashboard
      properties:
        dashboard_id:
          $ref: '#/components/schemas/dashboardID'
        group_id:
          $ref: '#/components/schemas/dashboardGroupID'
      required:
        - dashboard_id
    ValidatorsByDeposit:
      type: object
      title: 💎 Deposit
      properties:
        deposit_address:
          $ref: '#/components/schemas/ExecutionLayerAddress'
      required:
        - deposit_address
    ValidatorsByWithdrawal:
      type: object
      title: 💎 Withdrawal
      properties:
        withdrawal:
          $ref: '#/components/schemas/AddressOrCredential'
      required:
        - withdrawal
    ValidatorsByEntity:
      type: object
      title: 💎 Entity
      description: >
        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.
      properties:
        entity:
          type: string
          description: >
            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:
          type: string
          description: >
            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.
      required:
        - entity
    ProposalTimingEvaluationWindow:
      type: string
      enum:
        - 24h
        - 7d
        - 30d
      x-enum-varnames:
        - ProposalTimingWindow24h
        - ProposalTimingWindow7d
        - ProposalTimingWindow30d
      example: 30d
      description: >
        The rolling window to aggregate the proposals over. The window ends at
        the last finalized slot, moves every epoch (~6.4 minutes) and starts no
        earlier than the Glamsterdam hard fork.


        - `24h`: Last 24 hours (rolling)

        - `7d`: Last 7 days (rolling)

        - `30d`: Last 30 days (rolling)
    ValidatorProposalTiming.PublishTime:
      type: object
      description: >-
        When the proposal blocks of the validators first reached one
        beaconcha.in node over the peer-to-peer gossip network, next to the
        whole network over the same window.
      properties:
        attestation_deadline_ms:
          type: integer
          description: >-
            The attestation deadline in milliseconds after the slot start,
            `ATTESTATION_DUE_BPS_GLOAS` of the slot duration, which is 3000 at
            12-second slots. Fork choice counts a block as timely only when it
            arrives before this deadline.
        median_slot_offset_vs_network_ms:
          type: integer
          nullable: true
          description: >-
            The median, over the validators' proposals seen on gossip, of each
            proposal's `slot_offset_ms` minus the network median of the same
            hour of slot start. Negative means the validators' blocks reached
            our node earlier than the network's. The network median includes the
            validators' own proposals. Null when
            `validators.seen_on_gossip_count` is below 20.
        validators:
          $ref: '#/components/schemas/PublishTimeAggregate'
        network:
          $ref: '#/components/schemas/PublishTimeAggregate'
      required:
        - attestation_deadline_ms
        - median_slot_offset_vs_network_ms
        - validators
        - network
    SlotRange:
      type: object
      properties:
        start:
          $ref: '#/components/schemas/Slot'
        end:
          $ref: '#/components/schemas/Slot'
      required:
        - start
        - end
    EpochRange:
      type: object
      properties:
        start:
          $ref: '#/components/schemas/Epoch'
        end:
          $ref: '#/components/schemas/Epoch'
      required:
        - start
        - end
    TimeRange:
      type: object
      properties:
        start:
          $ref: '#/components/schemas/timestamp'
        end:
          $ref: '#/components/schemas/timestamp'
      required:
        - start
        - end
    validatorIndexPublicKeys:
      description: >
        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.
      type: array
      items:
        $ref: '#/components/schemas/validatorIndexPublicKey'
      minItems: 1
      maxItems: 100
    dashboardID:
      description: >
        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).
      type: integer
      x-go-type: '*int'
      minimum: 0
    dashboardGroupID:
      description: >-
        Optional beaconcha.in dashboard group ID. If no group ID is provided,
        all validators in the dashboard are considered.
      type: integer
      minimum: 0
      nullable: true
    ExecutionLayerAddress:
      type: string
      pattern: ^0x[a-fA-F0-9]{40}$
      description: A standard Ethereum address (20-byte hex string with 0x prefix).
    AddressOrCredential:
      type: string
      pattern: ^(0x)?[0-9a-fA-F]{40}$|^(0x)?0[012][0-9a-fA-F]{62}$
      description: >
        Either an execution layer address (20-byte hex string with 0x prefix) or
        a full 32-byte withdrawal credential.
    PublishTimeAggregate:
      type: object
      description: >-
        When a set of proposals reached one beaconcha.in node. A proposal counts
        once its slot is finalized, if the chain included its block or our node
        received a block for it. A missed slot for which our node received no
        block was never published and does not count.
      properties:
        proposal_count:
          type: integer
          minimum: 0
          description: >-
            Proposals that count. The sum of `seen_on_gossip_count`,
            `not_seen_on_gossip_count`, `not_observed_count` and
            `multiple_blocks_seen_count`.
        seen_on_gossip_count:
          type: integer
          minimum: 0
          description: >-
            Proposals whose block our node received over the peer-to-peer gossip
            network. Only these have a time, and the medians and the percentile
            use only them.
        median_slot_offset_ms:
          type: integer
          nullable: true
          description: >-
            Median milliseconds after the slot start at which our node received
            the blocks over gossip, by our node's clock. Null when
            `seen_on_gossip_count` is below 20.
        p90_slot_offset_ms:
          type: integer
          nullable: true
          description: >-
            90th percentile of the milliseconds after the slot start at which
            our node received the blocks over gossip. Null when
            `seen_on_gossip_count` is below 50.
        seen_after_attestation_deadline_count:
          type: integer
          minimum: 0
          description: >-
            Proposals whose block our node received at or after the attestation
            deadline. It describes when our node received the block, not whether
            the chain counted the block as timely.
        not_seen_on_gossip_count:
          type: integer
          minimum: 0
          description: >-
            Proposals whose block our node imported without receiving it over
            gossip, for example while it was syncing or before it had verified
            the parent block's execution payload. The usual cause is our own
            node, not the proposer.
        not_observed_count:
          type: integer
          minimum: 0
          description: >-
            Included proposals of which our node has no record, for example
            during an outage of our node.
        multiple_blocks_seen_count:
          type: integer
          minimum: 0
          description: >-
            Proposals for whose slot our node received more than one block. They
            have no time, because the figures cannot tell which of the blocks
            the chain included.
        not_included_count:
          type: integer
          minimum: 0
          description: >-
            Proposals whose block our node received but the chain did not
            include, as an orphaned block or for a slot the explorer shows as
            missed. They count in the other figures as well. The arrival time
            alone does not show why the chain did not include a block.
        not_included_seen_after_attestation_deadline_count:
          type: integer
          minimum: 0
          description: >-
            The part of `not_included_count` whose block our node received at or
            after the attestation deadline.
      required:
        - proposal_count
        - seen_on_gossip_count
        - median_slot_offset_ms
        - p90_slot_offset_ms
        - seen_after_attestation_deadline_count
        - not_seen_on_gossip_count
        - not_observed_count
        - multiple_blocks_seen_count
        - not_included_count
        - not_included_seen_after_attestation_deadline_count
    Slot:
      type: integer
      minimum: 0
      description: Slot by number.
    Epoch:
      type: integer
      minimum: 0
    timestamp:
      type: integer
      minimum: 0
    validatorIndexPublicKey:
      oneOf:
        - $ref: '#/components/schemas/validatorIndex'
        - $ref: '#/components/schemas/validatorPublicKey'
    validatorIndex:
      description: Validator Index
      type: integer
      minimum: 0
    validatorPublicKey:
      type: string
      description: Public key of a validator
      pattern: ^0x[a-fA-F0-9]{96}$
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      description: >-
        Authorization header with value: Bearer YOUR_TOKEN. Refer to the [API
        Keys](/api/overview#api-keys) section to create your API key.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.