Staking Data
Staking Data API

Querying Data

Learn how to query the Staking Data API — from simple queries to advanced filtering, sorting, and historical data.

The Staking Data API uses GraphQL. You send queries via POST to the API endpoint, selecting exactly the fields you need. Every query — at every nesting level — requires a limit argument.

Essentials

The Limit Property

Every level of the query requires a limit value. If you don't define a limit, your query won't work. A limit is required, even if it's obvious the output will only be one element!

The API uses limit properties to enhance speed and efficiency through four mechanisms:

  1. Performance optimization — Restricting returned data volume improves speed, particularly for large datasets, by reducing transmission requirements.

  2. User-controlled data — The limit field enables users to manage data quantity and prevent information overload.

  3. Bandwidth optimization — Mobile and low-bandwidth scenarios benefit from reduced data transmission, improving API performance and user data usage.

  4. Pagination and offset — Combining limit with offset enables pagination, allowing large datasets to be divided into manageable sections.

Limit at every query level
{
  rewardOptions(
    where: { inputAsset: { symbols: ["ETH"] }, typeKeys: ["pos"] }
    limit: 1
  ) {
    metrics(
      where: { metricKeys: ["reward_rate"], createdAt_lt: "2023-01-01" }
      limit: 5
    ) {
      metricKey
      defaultValue
      changePercentages
      createdAt
    }
  }
}

The isActive Flag

The API employs an isActive flag to exclude inactive Assets, Reward Options, Providers, and Validators.

ValueDescription
TrueOnly return active items (default)
FalseOnly return inactive items
AnyReturn all items regardless of active status

isActive is set to True by default. This means explicitly including where: {isActive: True} in queries is unnecessary.

Using isActive: Any
{
  assets(where: { slugs: ["ethereum"], isActive: Any }, limit: 1) {
    metrics(limit: 10) {
      defaultValue
      createdAt
      id
    }
  }
}

This query retrieves metrics for Ethereum regardless of its active status by using isActive: Any.

Simple Queries

You can fetch a single data entry using a simple object query. Any argument has to be an array (except booleans).

{
  assets(where: { symbols: ["ETH"] }, limit: 1) {
    id
    name
    slug
    description
    symbol
  }
}
{
  "data": {
    "assets": [
      {
        "id": "asset-id",
        "name": "Ethereum",
        "slug": "ethereum-2-0",
        "description": "the worlds largest and most decentralised Layer1 blockchain...",
        "symbol": "ETH"
      }
    ]
  }
}

Response Structure

The response contains a top-level data field with nested results. The assets field contains an array of asset objects with the requested fields like id, name, slug, description, and symbol.

Accessing Response Data

Using forEach loop:

Iterate over results
data.assets.forEach(asset => {
  console.log(asset.name, asset.symbol);
});

Using array destructuring:

Destructure first result
const [ethereum] = data.assets;
console.log(ethereum.name); // "Ethereum"

Remember that limit is required for all queries, even when you expect only one result.

Filtering Results

The where clause lets you narrow down results by matching fields against specific values. Every filterable field accepts an array of values (except booleans like isActive), and the API returns entries that match any of the provided values.

How Filters Work

Filters are passed as key-value pairs inside the where argument. You can filter on top-level fields (like symbols, slugs, or ids) as well as on nested relationships (like inputAsset). When you provide multiple keys inside a single where, they combine with AND logic — every condition must be satisfied for an item to be returned.

For array-valued filters (e.g., symbols: ["ETH", "SOL"]), the API uses OR logic within that array — it returns items matching any of the listed values.

Common Filter Fields

EntityCommon Filters
assetssymbols, slugs, ids, isActive
rewardOptionsinputAsset: { symbols }, typeKeys, isActive
providersnames, slugs, isActive
metricsmetricKeys, createdAt_gt, createdAt_lt
validatorsaddresses, isActive

Example

Filter by metric keys
{
  assets(where: {symbols: ["ETH"]}, limit: 1) {
    id
    name
    slug
    symbol
    metrics(where: {metricKeys: ["price", "staking_marketcap"]}, limit: 2) {
      metricKey
      unit
      defaultValue
      changePercentages
    }
  }
}

This query filters assets to only Ethereum (symbols: ["ETH"]) and then further filters its metrics to return only the price and staking_marketcap metric keys, limiting the result to 2 metrics.

Filters on nested objects (like metrics within assets) work independently — each level has its own where clause and limit.

Sorting Results

The order argument organizes results based on specified values. Sort direction options include ascending (asc) or descending (desc), using the format {field_name: order}. Execution sequence determines prioritization — earlier values take precedence.

Basic Sorting

Sort by launch status and name
{
  assets(order: { isLaunched: desc, name: asc }, limit: 10) {
    name
    symbol
    slug
    isLaunched
  }
}

This demonstrates sorting assets alphabetically by name, with launched assets appearing first.

Advanced Sorting with Metrics

Ordering supports changePercentagesKey or changeAbsolutesKey combined with metricKey_asc or metricKey_desc, where direction derives from metricKey.

Rank by 30-day staking marketcap
{
  assets(
    order: { metricKey_desc: "staking_marketcap", changePercentagesKey: _30d }
    limit: 10
  ) {
    id
    name
    slug
    description
    symbol
    metrics(where: { metricKeys: ["staking_marketcap"] }, limit: 10) {
      metricKey
      defaultValue
      changePercentages
    }
  }
}

This demonstrates ranking the top 10 assets by staking marketcap using 30-day percentage changes.

Sort Options

FieldDescription
metricKey_ascSort by metric value ascending
metricKey_descSort by metric value descending
changePercentagesKeySort by percentage change over a period
changeAbsolutesKeySort by absolute change over a period
nameSort alphabetically by name
addressSort by address (validators)
createdAtSort by creation date

Time Periods for Change Sorting

When using changePercentagesKey or changeAbsolutesKey:

PeriodDuration
_24h24 hours
_7d7 days
_30d30 days
_90d90 days
_1y1 year

Nested Queries

Nested queries let you traverse relationships between objects in a single request, building hierarchical structures that follow the data model.

The maximum nesting depth is 2.

Fetch the Allnodes provider with its associated reward options and validators:

Nested provider query
{
  providers(where: { names: ["Allnodes"] }, limit: 1) {
    id
    name
    slug
    rewardOptions(where: { typeKeys: ["pos"] }, limit: 10) {
      id
      inputAssets(limit: 10) {
        name
      }
      validators(limit: 10) {
        id
        address
      }
    }
  }
}

How It Works

  1. The query starts at the providers level, filtering for "Allnodes"
  2. Within each provider, it retrieves rewardOptions filtered by type
  3. For each reward option, it fetches inputAssets and validators

This nested structure allows you to get related data in a single request instead of making multiple API calls.

Always specify limit at each level of nesting to control the amount of data returned and optimize performance.

Combining Multiple Arguments

You can combine multiple filters and arguments in a single query to retrieve precisely the data you need. This query retrieves reward options with multiple filters — specific input assets and type keys — while returning details about the type, metrics, input assets, and providers.

Combined filters and arguments
{
  rewardOptions(where: {inputAsset: {symbols: ["ETH"]}, typeKeys: ["solo-staking", "pos"]}, limit: 10) {
    type {
      key
      label
    }
    metrics(limit: 10) {
      metricKey
      defaultValue
    }
    inputAssets(limit: 1) {
      name
      symbol
    }
    providers(limit: 5) {
      name
      country
    }
  }
}

This query demonstrates:

  • Filtering by input asset symbol (ETH)
  • Filtering by multiple type keys (solo-staking and pos)
  • Limiting results at each nesting level
  • Retrieving related objects (type, metrics, inputAssets, providers)

Multiple Queries in One Request

You can combine multiple queries into one request to maximize efficiency. Both queries execute in parallel and the response contains separate data for each.

Batch assets and global metrics
{
  assets(order: { name: asc }, limit: 10) {
    name
    symbol
    slug
  }

  metrics(
    where: {
      asset: null
      provider: null
      rewardOption: null
      validator: null
      metricKeys: ["marketcap"]
    }
    limit: 1
  ) {
    defaultValue
    changeAbsolutes
    changePercentages
    createdAt
  }
}

This request contains two distinct queries:

  1. First query — Retrieves 10 assets sorted by name (ascending), returning name, symbol, and slug fields
  2. Second query — Fetches global metrics with specific filters (null parameters for asset, provider, rewardOption, validator) and metricKeys of ["marketcap"], returning defaultValue, changeAbsolutes, changePercentages, and createdAt

Historical Data

For historical data, the createdAt_gt filter is needed. Without it, you only get the latest value. The required date format is YYYY-MM-DD.

Credit Cost for Historical Queries

Historical data queries incur a flat 5,000-credit surcharge in addition to the normal per-field cost (3 credits per metrics leaf). This reflects the higher value of historical data. The fields changePercentages and changeAbsolutes are not available for historical queries — including them returns a 400 error.

Reward Rate History

Retrieve historical reward rate data for Ethereum:

Historical ETH reward rates
{
  rewardOptions(where: {inputAsset: {symbols: ["ETH"]}, typeKeys: ["solo-staking", "pos"]}, limit: 10) {
    metrics(where: { metricKeys: ["reward_rate"], createdAt_lt: "2023-01-01" }, limit: 10) {
      metricKey
      defaultValue
      createdAt
    }
  }
}

Daily Interval Data

Query daily historical data for ETH reward rates starting from a specific date:

Daily interval reward rates
{
  rewardOptions(where: {inputAsset: {symbols: ["ETH"]}}, limit: 1) {
    metrics(where: { metricKeys: ["reward_rate"], createdAt_gt: "2023-01-01" }, interval: day, limit: 500) {
      metricKey
      defaultValue
      createdAt
    }
  }
}

Weekly Asset Metrics

Query weekly price metrics for an asset between specific dates:

Weekly price with date range
{
  assets(where: {ids: ["60a27c3d4d60300008d25ecf"]}, limit: 1){
    slug
    metrics(limit:500, where:{metricKeys: ["price"], createdAt_gt: "2023-01-01", createdAt_lt: "2023-09-07"}, order: {createdAt: desc}, interval: week, pickItem: last) {
      metricKey
      defaultValue
      createdAt
    }
  }
}

Technical Limitations

  • Interval queries work only for single resources
  • Results are capped at 500 data points globally
  • Supported intervals: hour, day, week, month, quarter

Using Variables

You can use GraphQL variables to parameterize your queries:

Variables:

Query variables
{
  "slugs": ["ethereum-2-0"],
  "limit": 100,
  "offset": 0,
  "metricKeys": ["reward_rate"],
  "timeStart": "2023-01-01"
}

Query:

Parameterized historical query
query getHistoricalMetrics($slugs: [String!], $limit: Int, $offset: Int, $metricKeys: [String!], $timeStart: Date) {
  assets(where: {slugs: $slugs}, limit: 1) {
    metrics(where: {metricKeys: $metricKeys, createdAt_gt: $timeStart}, limit: $limit, offset: $offset, order: {createdAt: asc}) {
      defaultValue
      createdAt
      id
    }
  }
}

Date Filters

FilterDescription
createdAt_gtGreater than (after) the specified date
createdAt_ltLess than (before) the specified date
createdAt_gteGreater than or equal to the specified date
createdAt_lteLess than or equal to the specified date

Interval Options

IntervalDescription
hourHourly data points
dayDaily data points
weekWeekly data points
monthMonthly data points
quarterQuarterly data points

On this page