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:
-
Performance optimization — Restricting returned data volume improves speed, particularly for large datasets, by reducing transmission requirements.
-
User-controlled data — The limit field enables users to manage data quantity and prevent information overload.
-
Bandwidth optimization — Mobile and low-bandwidth scenarios benefit from reduced data transmission, improving API performance and user data usage.
-
Pagination and offset — Combining limit with offset enables pagination, allowing large datasets to be divided into manageable sections.
{
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.
| Value | Description |
|---|---|
True | Only return active items (default) |
False | Only return inactive items |
Any | Return all items regardless of active status |
isActive is set to True by default. This means explicitly including where: {isActive: True} in queries is unnecessary.
{
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:
data.assets.forEach(asset => {
console.log(asset.name, asset.symbol);
});Using array destructuring:
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
| Entity | Common Filters |
|---|---|
assets | symbols, slugs, ids, isActive |
rewardOptions | inputAsset: { symbols }, typeKeys, isActive |
providers | names, slugs, isActive |
metrics | metricKeys, createdAt_gt, createdAt_lt |
validators | addresses, isActive |
Example
{
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
{
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.
{
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
| Field | Description |
|---|---|
metricKey_asc | Sort by metric value ascending |
metricKey_desc | Sort by metric value descending |
changePercentagesKey | Sort by percentage change over a period |
changeAbsolutesKey | Sort by absolute change over a period |
name | Sort alphabetically by name |
address | Sort by address (validators) |
createdAt | Sort by creation date |
Time Periods for Change Sorting
When using changePercentagesKey or changeAbsolutesKey:
| Period | Duration |
|---|---|
_24h | 24 hours |
_7d | 7 days |
_30d | 30 days |
_90d | 90 days |
_1y | 1 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:
{
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
- The query starts at the
providerslevel, filtering for "Allnodes" - Within each provider, it retrieves
rewardOptionsfiltered by type - For each reward option, it fetches
inputAssetsandvalidators
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.
{
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-stakingandpos) - 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.
{
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:
- First query — Retrieves 10 assets sorted by name (ascending), returning name, symbol, and slug fields
- 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:
{
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:
{
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:
{
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:
{
"slugs": ["ethereum-2-0"],
"limit": 100,
"offset": 0,
"metricKeys": ["reward_rate"],
"timeStart": "2023-01-01"
}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
| Filter | Description |
|---|---|
createdAt_gt | Greater than (after) the specified date |
createdAt_lt | Less than (before) the specified date |
createdAt_gte | Greater than or equal to the specified date |
createdAt_lte | Less than or equal to the specified date |
Interval Options
| Interval | Description |
|---|---|
hour | Hourly data points |
day | Daily data points |
week | Weekly data points |
month | Monthly data points |
quarter | Quarterly data points |