Build a production-aware restaking and LRT monitoring system using CoinMarketCap API to track EtherFi, ETHFI, eETH, weETH, and broader yield rotation signals.
Introduction
Restaking has become one of the most important DeFi narratives.
EtherFi sits at the center of that narrative through liquid restaking exposure and the ETHFI ecosystem.
But tracking EtherFi is not only about watching the ETHFI price.
A useful tracker should monitor:
- ETHFI momentum
- eETH / weETH market behavior
- liquid restaking token activity
- ETH beta
- restaking sector strength
- liquidity quality
- market regime
CoinMarketCap API gives you the off-chain signal layer required to build this kind of tracker.
- CoinMarketCap powers the market intelligence layer
- your system monitors restaking and LRT momentum
- EtherFi-related assets become part of a broader yield rotation dashboard
This guide is educational and production-aware. It is not financial advice.
Why Use CoinMarketCap API for EtherFi Tracking?
Restaking assets behave differently from normal spot tokens.
They are influenced by:
- ETH market conditions
- liquid restaking demand
- DeFi risk appetite
- liquidity depth
- protocol narratives
- sector-wide capital rotation
CoinMarketCap API provides:
- asset mapping
- current quotes
- categories
- listings
- historical quotes
- market-pair liquidity context
- Fear & Greed data
- Altcoin Season data
This allows your tracker to move from:
“ETHFI price changed”
toward:
“capital is rotating into restaking and LRT assets”
System Architecture
CoinMarketCap API
├─ Cryptocurrency Map
├─ Quotes Latest
├─ Listings Latest
├─ Categories / Category
├─ Historical Quotes
├─ Market Pairs
├─ Fear & Greed
└─ Altcoin Season
↓
EtherFi Yield Tracker
↓
Restaking / LRT Momentum Score
↓
Watchlist / Alerts / Dashboard
Important Architecture Clarification
It is not a real-time, on-chain execution oracle or yield state machine.
Advanced decentralized strategies such as Pendle yield-stripping, LRT looping, or cross-chain messaging must validate exact smart contract reserves, staking caps, and on-chain slippage locally before execution to account for REST API cache delays.
Project Setup
import os
import time
import requests
import pandas as pd
import numpy as np
CMC_API_KEY = os.getenv("CMC_API_KEY")
CMC_BASE_URL = "https://pro-api.coinmarketcap.com"
HEADERS = {
"Accept": "application/json",
"X-CMC_PRO_API_KEY": CMC_API_KEY,
}
Step 1: Map EtherFi and LRT Assets
Use CoinMarketCap IDs rather than symbols whenever possible.
Symbols can collide. IDs are stable.
Endpoint:
/v1/cryptocurrency/map
Use it to map assets such as:
- ETHFI
- eETH
- weETH
- stETH
- ezETH
- rsETH
- ETH
Example
def map_assets(symbols="ETHFI,ETH"):
url = f"{CMC_BASE_URL}/v1/cryptocurrency/map"
params = {
"symbol": symbols
}
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=20
)
r.raise_for_status()
return r.json()["data"]
For tokens, the response may include a platform object containing the token contract address.
Native assets generally have platform = null.
Step 2: Fetch Quotes for ETHFI and LRT Assets
Endpoint:
/v3/cryptocurrency/quotes/latest
This endpoint supports batch requests with comma-separated IDs.
Example
def fetch_quotes(ids):
url = f"{CMC_BASE_URL}/v3/cryptocurrency/quotes/latest"
params = {
"id": ids
}
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=20
)
r.raise_for_status()
return r.json()["data"]
Step 3: Parse quote Correctly
In the real v3 API response, quote is a list of quote objects.
Do not parse it as a dictionary keyed by USD.
Incorrect:
usd = asset["quote"]["USD"]
Correct:
quotes = asset.get("quote", [])
usd = next((q for q in quotes if q.get("symbol") == "USD"), {})
Normalization Helper
def get_usd_quote(asset):
quotes = asset.get("quote", [])
return next(
(q for q in quotes if q.get("symbol") == "USD"),
{}
)
def normalize_asset(asset):
usd = get_usd_quote(asset)
return {
"id": asset.get("id"),
"symbol": asset.get("symbol"),
"name": asset.get("name"),
"price": usd.get("price"),
"volume_24h": usd.get("volume_24h") or 0,
"market_cap": usd.get("market_cap") or 0,
"fully_diluted_market_cap": usd.get("fully_diluted_market_cap") or 0,
"percent_change_1h": usd.get("percent_change_1h") or 0,
"percent_change_24h": usd.get("percent_change_24h") or 0,
"percent_change_7d": usd.get("percent_change_7d") or 0,
"tvl": usd.get("tvl"), "tvl_ratio": asset.get("tvl_ratio"),
"last_updated": asset.get("last_updated"),
}
Important:
- tvl lives inside the USD quote object when available
- tvl_ratio lives at the root asset level
- both can return null
Treat TVL fields as optional enrichment.
Step 4: Discover Restaking Categories
Use categories to discover restaking and liquid staking sectors dynamically.
Endpoint:
/v1/cryptocurrency/categories
Do not hardcode category IDs. Fetch them dynamically.
Example
def fetch_categories():
url = f"{CMC_BASE_URL}/v1/cryptocurrency/categories"
r = requests.get(
url,
headers=HEADERS,
timeout=30
)
r.raise_for_status()
return r.json()["data"]
Find Restaking / LRT Categories
def find_restaking_categories(categories):
keywords = [
"restaking",
"liquid restaking",
"liquid staking",
"staking",
"yield",
]
matches = []
for category in categories:
searchable = " ".join([
str(category.get("name", "")),
str(category.get("title", "")),
str(category.get("description", "")),
]).lower()
if any(k in searchable for k in keywords):
matches.append({
"id": category.get("id"),
"name": category.get("name"),
"title": category.get("title"),
"num_tokens": category.get("num_tokens"),
"market_cap": category.get("market_cap"),
"volume": category.get("volume"),
"avg_price_change": category.get("avg_price_change"),
"market_cap_change": category.get("market_cap_change"),
})
return pd.DataFrame(matches)
Step 5: Fetch Tokens From a Category
Once you identify the correct restaking or liquid staking category, fetch the assets inside it.
Endpoint:
/v1/cryptocurrency/category
This endpoint returns category-level metrics and the underlying crypto assets.
Useful category fields include:
- id
- name
- title
- description
- num_tokens
- market_cap
- volume
- avg_price_change
- market_cap_change
Example
def fetch_category(category_id):
url = f"{CMC_BASE_URL}/v1/cryptocurrency/category"
params = {
"id": category_id
}
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=30
)
r.raise_for_status()
return r.json()["data"]
Step 6: Use Listings for Yield Rotation
For a broader yield rotation screen, use:
/v3/cryptocurrency/listings/latest
A Basic-friendly approach is to pull a broad DeFi universe and filter locally.
def fetch_defi_universe(limit=500):
url = f"{CMC_BASE_URL}/v3/cryptocurrency/listings/latest"
params = {
"limit": limit,
"sort": "volume_24h",
"tag": "defi",
"aux": "tags"
}
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=30
)
r.raise_for_status()
return r.json()["data"]
This is often more efficient than repeatedly calling many category endpoints.
Use categories for clean sector discovery.
Use listings for large-scale rotation monitoring.
Step 7: Build an LRT Momentum Score
The tracker should measure restaking momentum, not just raw price change.
Useful inputs:
- 24h volume
- 24h price change
- 7d price change
- market cap
- fully diluted market cap
- TVL when available
- ETH relative strength
Example
def lrt_momentum_score(asset):
volume = asset.get("volume_24h") or 0
p24h = asset.get("percent_change_24h") or 0
p7d = asset.get("percent_change_7d") or 0
market_cap = asset.get("market_cap") or 0
fdv = asset.get("fully_diluted_market_cap") or 0
volume_score = min(volume / 100_000_000, 1)
momentum_score = max(min(p24h / 20, 1), -1)
trend_score = max(min(p7d / 30, 1), -1)
dilution_penalty = 0
if market_cap > 0 and fdv > 0:
fdv_ratio = fdv / market_cap
if fdv_ratio > 5:
dilution_penalty = 0.2
return (
volume_score * 0.35 +
momentum_score * 0.30 +
trend_score * 0.25 -
dilution_penalty
)
For yield and restaking assets, monitor both market cap and fully diluted market cap. FDV can reveal future dilution risk.
Step 8: Add ETH Relative Strength
Restaking assets are ETH-linked.
A useful tracker should compare ETHFI or LRT tokens against ETH.
def relative_strength(asset_return, eth_return):
return asset_return - eth_return
Example:
ethfi_rs = relative_strength(
asset_return=12.5,
eth_return=3.0
)
A positive relative strength value suggests the asset is outperforming ETH.
This helps distinguish:
- ETH market beta
- restaking-specific momentum
Step 9: Add Market Regime Filters
Restaking demand changes with market regime.
Fear & Greed
Endpoint:
/v3/fear-and-greed/latest
def fetch_fear_greed():
url = f"{CMC_BASE_URL}/v3/fear-and-greed/latest"
r = requests.get(
url,
headers=HEADERS,
timeout=15
)
r.raise_for_status()
data = r.json()["data"]
return {
"value": data["value"],
"classification": data["value_classification"],
"update_time": data["update_time"],
}
Altcoin Season Index
Endpoint:
/v1/altcoin-season-index/latest
def fetch_altcoin_index():
url = f"{CMC_BASE_URL}/v1/altcoin-season-index/latest"
r = requests.get(
url,
headers=HEADERS,
timeout=15
)
r.raise_for_status()
return r.json()["data"]["altcoin_index"]
Official interpretation:
- altcoin_index > 75 → Altcoin Season
- altcoin_index < 25 → Bitcoin Season
Step 10: Evaluate Liquidity Quality
For execution-quality context, use:
/v2/cryptocurrency/market-pairs/latest
This endpoint is useful for exchange-level market quality.
Useful fields:
- effective_liquidity
- market_score
- market_reputation
- depth_negative_two
- depth_positive_two
Request enrichment fields through aux:
def fetch_market_pairs(asset_id):
url = f"{CMC_BASE_URL}/v2/cryptocurrency/market-pairs/latest"
params = {
"id": asset_id,
"limit": 100,
"aux": "effective_liquidity,market_score,market_reputation"
}
try:
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=30
)
r.raise_for_status()
return r.json()["data"]
except requests.HTTPError as e:
if e.response is not None and e.response.status_code == 403:
print("Market pairs unavailable on this plan")
return None
raise
Important plan warning:
This endpoint may return:
HTTP 403 Forbidden
Error 1006: Plan Not Authorized
on Basic plans.
Treat market-pair liquidity as optional enrichment.
Basic-friendly fallback:
- continue using /v3/cryptocurrency/listings/latest
- filter by volume_24h
- filter by market_cap
- mark exchange-level liquidity as unavailable
Depth and liquidity fields may also return null, especially for newer LRTs or DEX-only assets.
Parse defensively.
Step 11: Historical Backtesting
Use historical quotes for regime and relative strength analysis.
Endpoint:
/v3/cryptocurrency/quotes/historical
Useful intervals:
- 1h
- 4h
- daily
def fetch_historical_quotes(asset_id, time_start, time_end, interval="daily"):
url = f"{CMC_BASE_URL}/v3/cryptocurrency/quotes/historical"
params = {
"id": asset_id,
"time_start": time_start,
"time_end": time_end,
"interval": interval,
}
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=30
)
r.raise_for_status()
return r.json()["data"]
Important:
- historical cache updates roughly every 5 minutes
- historical data costs 1 credit per 100 data points
- deep history depends on plan tier
- Basic access may be limited for longer backtests
Use historical data for bootstrap and backtesting, not fast polling.
Step 12: Paid Trending Endpoints
Paid endpoints can help detect narrative attention.
Examples:
/v1/cryptocurrency/trending/latest
/v1/cryptocurrency/trending/gainers-losers
/v1/cryptocurrency/trending/most-visited
/v1/community/trending/topic
/v1/community/trending/token
These endpoints can help identify:
- search-volume momentum
- retail attention
- community narrative strength
- restaking topic momentum
Paid plan warning:
HTTP 403 Forbidden
Error 1006: Plan Not Authorized
Basic-friendly fallback:
- use /v3/cryptocurrency/listings/latest
- sort by volume_24h
- filter locally using tags and restaking-related assets
Step 13: Minimal End-to-End Flow
def run_etherfi_tracker(asset_ids):
raw_quotes = fetch_quotes(asset_ids)
normalized = [
normalize_asset(asset)
for asset in raw_quotes
]
df = pd.DataFrame(normalized)
df["lrt_score"] = df.apply(
lambda row: lrt_momentum_score(row.to_dict()),
axis=1
)
return df.sort_values(
"lrt_score",
ascending=False
)
Rate Limits and Polling
Recommended cadence:
- quotes/latest → every 60 seconds
- listings/latest → every 60 seconds
- Fear & Greed → every 15 minutes
- Altcoin Season → every 15 minutes
- historical → bootstrap or backtesting only
Use local caching.
Use exponential backoff for HTTP 429.
def request_with_backoff(url, params=None):
failures = 0
while True:
try:
r = requests.get(
url,
headers=HEADERS,
params=params,
timeout=30
)
r.raise_for_status()
return r.json()
except requests.HTTPError as e:
if e.response is not None and e.response.status_code == 429:
failures += 1
time.sleep(min(60 * (2 ** failures), 900))
continue
raise
Common Mistakes
Parsing quote Incorrectly
In v3, quote is a list.
Correct:
quotes = asset.get("quote", [])
usd = next((q for q in quotes if q.get("symbol") == "USD"), {})
Incorrect:
usd = asset["quote"]["USD"]
Treating TVL Fields as Guaranteed
tvl and tvl_ratio can return null.
Use them as optional enrichment, not mandatory scoring inputs.
Ignoring FDV
Restaking governance tokens may have large future dilution.
Track both market_cap and fully_diluted_market_cap.
Overusing Historical Endpoints
Historical data is credit-intensive.
Use it for bootstrap and backtesting, not live loops.
Treating CMC as an Execution Oracle
CoinMarketCap is an off-chain signal layer.
Always validate real smart contract state before any on-chain execution.
Final Thoughts
EtherFi tracking is not just about ETHFI price.
A useful tracker should combine:
- restaking category momentum
- ETH relative strength
- LRT liquidity
- FDV risk
- market regime
- historical context
CoinMarketCap API gives you the market intelligence layer required to build that system.
The result is a cleaner and more production-aware restaking dashboard.
Next Steps
To improve the tracker:
- add on-chain eETH / weETH supply data
- compare LRTs against ETH
- monitor Pendle PT/YT markets
- add alerts for category momentum
- add liquidity-quality thresholds
- store historical snapshots locally
Better restaking intelligence leads to better yield decisions.
