ops-indexer-query

star 194

Internal — for Boundless team members only. Query the Boundless Indexer REST API for on-chain market data, staking, PoVW rewards, delegations, and market efficiency on prod/staging environments. Use when the user asks about proof requests, provers, requestors, staking data, PoVW rewards, delegations, market aggregates, leaderboards, or wants to fetch data from the indexer API on live networks. Also use when the user mentions "indexer" and wants to look up addresses, requests, or market statistics on deployed environments. Do NOT use for debugging local code changes, reviewing PRs, or investigating issues in the codebase itself.

boundless-xyz By boundless-xyz schedule Updated 4/14/2026

name: ops-indexer-query description: Internal — for Boundless team members only. Query the Boundless Indexer REST API for on-chain market data, staking, PoVW rewards, delegations, and market efficiency on prod/staging environments. Use when the user asks about proof requests, provers, requestors, staking data, PoVW rewards, delegations, market aggregates, leaderboards, or wants to fetch data from the indexer API on live networks. Also use when the user mentions "indexer" and wants to look up addresses, requests, or market statistics on deployed environments. Do NOT use for debugging local code changes, reviewing PRs, or investigating issues in the codebase itself.

Indexer Query

Query the Boundless Indexer REST API to retrieve on-chain data across multiple chains.

Two Types of Indexer

There are two distinct indexer types with different endpoint sets:

  1. Market Indexers -- Deployed per L2 chain. Provide market, requestor, prover, and efficiency endpoints.
  2. ZKC Indexers -- Deployed per ZKC token deployment (Ethereum L1). Provide staking, PoVW, and delegation endpoints.

These endpoint sets do not overlap. Market endpoints only work on market indexers; staking/PoVW/delegation endpoints only work on ZKC indexers.

Network Selection

If the user does not specify which network to query, ask them which network they want. Use the query topic to determine whether you need a market indexer or ZKC indexer (or both).

Market Indexers

Network Environment Base URL ZKC Source
Base prod https://d2mdvlnmyov1e1.cloudfront.net Eth Mainnet Prod
Taiko prod https://d29nqt0gudcxhl.cloudfront.net Eth Mainnet Prod
Eth Sepolia prod https://d3jjbcwhlw21k7.cloudfront.net Eth Sepolia Prod
Base Sepolia prod https://d3kkukmpiqlzm1.cloudfront.net Eth Sepolia Prod
Taiko staging https://d1wuyzcum0d4x4.cloudfront.net Eth Sepolia Staging
Base Sepolia staging https://d29i5cy5rhdodq.cloudfront.net Eth Sepolia Staging

Available endpoints: /v1/market/**, /v1/market/requestors/**, /v1/market/provers/**, /v1/market/efficiency/**

ZKC Indexers

Network Environment Base URL Serves ZKC for
Eth Mainnet prod https://d3ig5wxf8178te.cloudfront.net Base Prod, Taiko Prod
Eth Sepolia prod https://d3jjbcwhlw21k7.cloudfront.net Base Sepolia Prod
Eth Sepolia staging https://d3dtnqspyflstc.cloudfront.net Taiko Staging, Base Sepolia Staging

Available endpoints: /v1/staking/**, /v1/povw/**, /v1/delegations/**

Choosing the Right Indexer

  • For proof requests, provers, requestors, market stats, efficiency -> use the market indexer for the relevant L2 chain.
  • For staking, PoVW rewards, delegations -> use the ZKC indexer for the corresponding Ethereum deployment.
  • If the user asks about both (e.g. "show me prover X's market stats and staking info"), you need to query both the market indexer and the corresponding ZKC indexer.

Use prod URLs by default unless the user explicitly asks for staging.

Store the selected base URL(s) in shell variables for reuse:

export MARKET_INDEXER_URL="https://d2mdvlnmyov1e1.cloudfront.net"
export ZKC_INDEXER_URL="https://d3ig5wxf8178te.cloudfront.net"

Credentials

First, try to read network_secrets.toml from the repo root. If it exists, extract the indexer API key for the selected environment from [environments.<env>.indexer] api_key. Also read network_address_labels.json (same directory) for translating addresses to human-readable labels in output.

If network_secrets.toml is not present, recommend the user create it -- instructions and credentials are in the Boundless runbook. The API still works without a key, but they'll have lower rate limits.

If network_address_labels.json is not present, recommend the user create it -- the canonical address mapping is in the Boundless runbook. The file is plain JSON ({"0xaddr": "label", ...}).

When an API key is available:

export INDEXER_API_KEY="the-key"

Include it in requests via the x-api-key header. Without an API key, omit the header.

Known Addresses

If network_address_labels.json exists at the repo root, use it to label addresses in results. The file is plain JSON ({"0xaddr": "label", ...}) so the user can paste directly from the canonical mapping. When displaying addresses from API responses, check if the address (case-insensitive) has a known label and show it alongside, e.g. 0xbdA9...5542 (BP1).

Provers we operate are labeled with a BP prefix (e.g. BP1, BP2, BPNightlyAWS). When investigating any issue, always highlight what our BP provers are doing -- did they skip, fail, drop, or fulfill? This should be called out explicitly even when the investigation is not specifically about our provers.

Secondary Fulfillment

When a prover locks an order but fails to fulfill it, the order becomes available for secondary fulfillment by any other prover, who earns the slash collateral as reward. In the indexer, a secondary fulfillment is visible when fulfill_prover_address differs from lock_prover_address on a request. Market aggregates include a total_secondary_fulfillments field. When investigating expired or slashed requests, always check whether secondary fulfillment occurred or was attempted -- especially by our BP provers.

Rate Limiting

The API is protected by AWS WAF rate limits:

  • Without API key: 200 requests per IP per 5 minutes
  • With API key: 1000 requests per IP per 5 minutes

Self-rate-limit when making multiple requests:

  • Add sleep 1 between sequential requests
  • If a request returns HTTP 429 or 403, back off for 30 seconds before retrying

Making Requests

All endpoints return JSON. Use curl -s and pipe through jq for readability.

Define a helper function that takes a full URL:

indexer_get() {
  local url="$1"
  if [ -n "$INDEXER_API_KEY" ]; then
    curl -s -H "x-api-key: $INDEXER_API_KEY" "$url"
  else
    curl -s "$url"
  fi
}

Usage:

indexer_get "${MARKET_INDEXER_URL}/v1/market/requests?limit=10" | jq .
indexer_get "${ZKC_INDEXER_URL}/v1/staking/addresses" | jq .

Pagination

Two pagination styles exist:

Offset pagination (staking, PoVW, delegations -- ZKC indexer)

Uses limit (default 50, max 100) and offset (default 0). Response includes pagination: { count, offset, limit }.

indexer_get "${ZKC_INDEXER_URL}/v1/staking/addresses?limit=50&offset=0" | jq .
indexer_get "${ZKC_INDEXER_URL}/v1/staking/addresses?limit=50&offset=50" | jq .

Cursor pagination (market endpoints -- market indexer)

Uses limit and cursor (Base64 opaque token). Response includes has_more and next_cursor.

RESP=$(indexer_get "${MARKET_INDEXER_URL}/v1/market/requests?limit=50")
echo "$RESP" | jq .
NEXT=$(echo "$RESP" | jq -r '.next_cursor // empty')
if [ -n "$NEXT" ]; then
  sleep 1
  indexer_get "${MARKET_INDEXER_URL}/v1/market/requests?limit=50&cursor=$NEXT" | jq .
fi

To fetch all pages in a loop (with rate limiting):

CURSOR=""
PAGE=0
BASE_URL="${MARKET_INDEXER_URL}/v1/market/requests"
while true; do
  PARAMS="limit=50"
  [ -n "$CURSOR" ] && PARAMS="${PARAMS}&cursor=${CURSOR}"
  RESP=$(indexer_get "${BASE_URL}?${PARAMS}")
  echo "$RESP" | jq '.data | length' 
  CURSOR=$(echo "$RESP" | jq -r '.next_cursor // empty')
  HAS_MORE=$(echo "$RESP" | jq -r '.has_more')
  PAGE=$((PAGE + 1))
  [ "$HAS_MORE" = "false" ] || [ -z "$CURSOR" ] && break
  sleep 1
done
echo "Fetched $PAGE pages"

Filtering by Time Range

Not all endpoints support before/after query parameters. The aggregate and cumulative endpoints do, but request list endpoints (/v1/market/requests, /v1/market/provers/:address/requests, /v1/market/requestors/:address/requests) do not have server-side time filtering.

When you need requests from a specific time window on these endpoints, you must paginate and inspect timestamps client-side. Results are sorted by created_at descending (newest first) by default, so stop paginating once you see a created_at older than your cutoff:

CURSOR=""
CUTOFF="2026-03-25T00:00:00Z"
CUTOFF_UNIX=$(date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "$CUTOFF" +%s 2>/dev/null || date -d "$CUTOFF" +%s)
BASE_URL="${MARKET_INDEXER_URL}/v1/market/requests"
while true; do
  PARAMS="limit=50"
  [ -n "$CURSOR" ] && PARAMS="${PARAMS}&cursor=${CURSOR}"
  RESP=$(indexer_get "${BASE_URL}?${PARAMS}")

  OLDEST=$(echo "$RESP" | jq '[.data[].created_at] | min')
  if [ "$OLDEST" != "null" ] && [ "$OLDEST" -lt "$CUTOFF_UNIX" ]; then
    echo "Reached cutoff, stopping."
    echo "$RESP" | jq --argjson cutoff "$CUTOFF_UNIX" '.data | map(select(.created_at >= $cutoff))'
    break
  fi

  echo "$RESP" | jq '.data | length'
  CURSOR=$(echo "$RESP" | jq -r '.next_cursor // empty')
  HAS_MORE=$(echo "$RESP" | jq -r '.has_more')
  [ "$HAS_MORE" = "false" ] || [ -z "$CURSOR" ] && break
  sleep 1
done

Endpoints that do support before/after (Unix timestamps):

  • /v1/market/aggregates
  • /v1/market/cumulatives
  • /v1/market/requestors/:address/aggregates
  • /v1/market/requestors/:address/cumulatives
  • /v1/market/provers/:address/aggregates
  • /v1/market/provers/:address/cumulatives
  • /v1/market/efficiency/aggregates
  • /v1/market/efficiency/requests

Endpoints that require client-side time filtering:

  • /v1/market/requests
  • /v1/market/requestors/:address/requests
  • /v1/market/provers/:address/requests
  • All staking, PoVW, and delegation endpoints

Endpoint Reference

For the full list of endpoints, query parameters, and response schemas, read references/endpoints.md.

Quick Reference

Market Indexer endpoints (L2 chains: Base, Taiko, Base Sepolia):

Category Key Endpoints
Market status GET /v1/market
Requests GET /v1/market/requests, GET /v1/market/requests/:request_id
Market aggregates GET /v1/market/aggregates?aggregation=daily
Market cumulatives GET /v1/market/cumulatives
Requestor leaderboard GET /v1/market/requestors?period=7d
Prover leaderboard GET /v1/market/provers?period=7d
Requestor details GET /v1/market/requestors/:address/requests
Prover details GET /v1/market/provers/:address/requests
Efficiency GET /v1/market/efficiency

ZKC Indexer endpoints (Ethereum L1: Eth Mainnet, Eth Sepolia):

Category Key Endpoints
Staking summary GET /v1/staking
Staking leaderboard GET /v1/staking/addresses
PoVW summary GET /v1/povw
PoVW leaderboard GET /v1/povw/addresses
Delegations GET /v1/delegations/votes/addresses, GET /v1/delegations/rewards/addresses

Common Tasks

Look up a specific request (market indexer)

indexer_get "${MARKET_INDEXER_URL}/v1/market/requests/0xABC123" | jq .

Get recent requests (market indexer)

indexer_get "${MARKET_INDEXER_URL}/v1/market/requests?limit=20" | jq .

Get requests for a specific requestor (market indexer)

indexer_get "${MARKET_INDEXER_URL}/v1/market/requestors/0xADDRESS/requests?limit=20" | jq .

Get daily market stats for the last week (market indexer)

indexer_get "${MARKET_INDEXER_URL}/v1/market/aggregates?aggregation=daily&limit=7" | jq .

Get prover leaderboard for last 24h (market indexer)

indexer_get "${MARKET_INDEXER_URL}/v1/market/provers?period=1d" | jq .

Get staking info for an address (ZKC indexer)

indexer_get "${ZKC_INDEXER_URL}/v1/staking/addresses/0xADDRESS" | jq .

Get PoVW rewards for an address (ZKC indexer)

indexer_get "${ZKC_INDEXER_URL}/v1/povw/addresses/0xADDRESS" | jq .

Check market efficiency (market indexer)

indexer_get "${MARKET_INDEXER_URL}/v1/market/efficiency?type=gas_adjusted" | jq .

Error Handling

HTTP Status Meaning
200 Success
400 Bad request (invalid params)
403 WAF blocked (rate limited or bad key)
404 Endpoint or resource not found
429 Rate limited
502/503 Backend/database error (retry after delay)

Error responses return JSON: {"error": "...", "message": "..."}.

OpenAPI Spec

The full OpenAPI spec is available on any indexer at /openapi.yaml or interactively at /docs (Swagger UI). Fetch it if you need detailed field-level schema information:

indexer_get "${MARKET_INDEXER_URL}/openapi.yaml"
indexer_get "${ZKC_INDEXER_URL}/openapi.yaml"
Install via CLI
npx skills add https://github.com/boundless-xyz/boundless --skill ops-indexer-query
Repository Details
star Stars 194
call_split Forks 172
navigation Branch main
article Path SKILL.md
More from Creator
boundless-xyz
boundless-xyz Explore all skills →