
Defillama Api
- 202 installs
- 257 repo stars
- Updated June 24, 2026
- agiprolabs/claude-trading-skills
defillama-api is a Claude Code skill that queries the free DeFiLlama API for cross-chain DeFi analytics: TVL, token prices, DEX volumes, fees/revenue, stablecoins, and bridges.
About
defillama-api is a Claude Code skill that queries the DeFiLlama API for cross-chain DeFi analytics: TVL, token prices, DEX volumes, fees and revenue, stablecoins, and bridges. Most endpoints are free with no authentication. A developer uses it when building DeFi macro analysis into an agent or script. It documents base URLs, the {chain}:{address} coin identifier format, and rate limits.
- Queries DeFiLlama for TVL, token prices, DEX volumes, fees/revenue, stablecoins, bridges
- Free with no auth for most endpoints; Pro tier for yields and bridge detail
- Multi-chain coin identifiers in {chain}:{address} format
Defillama Api by the numbers
- 202 all-time installs (skills.sh)
- Ranked #465 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
defillama-api capabilities & compatibility
Free for most endpoints (no key); Pro key ($300/mo) needed for yields and bridge detail.
- Capabilities
- defi analytics · tvl lookup · token price fetch · dex volume data · stablecoin data
- Use cases
- data analysis · research · trading
- Runs
- Runs locally
- Pricing
- Freemium
What defillama-api says it does
Free DeFi analytics across all chains — TVL, token prices, DEX volumes, fees/revenue, stablecoins, and bridges
Its API is **free with no authentication** for most endpoints
npx skills add https://github.com/agiprolabs/claude-trading-skills --skill defillama-apiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 202 |
|---|---|
| repo stars | ★ 257 |
| Last updated | June 24, 2026 |
| Repository | agiprolabs/claude-trading-skills ↗ |
What it does
Query DeFiLlama for TVL, token prices, DEX volumes, fees, stablecoins, and bridges across chains.
Who is it for?
DeFi macro analytics such as protocol TVL, multi-chain token prices, and DEX volume/fee comparisons.
Skip if: Yields data, which the skill notes is a Pro ($300/mo) endpoint rather than free.
When should I use this skill?
You need DeFiLlama TVL, prices, DEX volume, or stablecoin data inside an agent or script.
What you get
Working DeFiLlama queries for TVL, prices, volumes, fees, and stablecoins with correct identifiers.
By the numbers
- Free rate limit ~500 requests per 5 minutes
- Yields endpoint is Pro at $300/mo
- Covers TVL, prices, DEX volumes, fees, stablecoins, and bridges
Files
DeFiLlama API — DeFi Macro Analytics
DeFiLlama is the largest DeFi TVL aggregator. Its API is free with no authentication for most endpoints — covering TVL, token prices, DEX volumes, fees/revenue, stablecoins, and bridges across all chains.
Quick Start
import httpx
# No auth required for free endpoints
BASE = "https://api.llama.fi"
COINS = "https://coins.llama.fi"
# Current TVL for a protocol
tvl = httpx.get(f"{BASE}/tvl/raydium").json()
print(f"Raydium TVL: ${tvl:,.0f}")
# Token prices (multi-chain)
resp = httpx.get(f"{COINS}/prices/current/solana:So11111111111111111111111111111111111111112")
sol_price = resp.json()["coins"]["solana:So11111111111111111111111111111111111111112"]["price"]Base URLs
| Service | Base URL | Auth |
|---|---|---|
| TVL / Protocols | https://api.llama.fi | Free |
| Coin Prices | https://coins.llama.fi | Free |
| Stablecoins | https://stablecoins.llama.fi | Free |
| Yields | https://yields.llama.fi | Pro ($300/mo) |
| Bridges | https://bridges.llama.fi | Free (list) / Pro (detail) |
| Pro API | https://pro-api.llama.fi/{KEY}/api/... | Pro key in URL path |
Rate limits: ~500 requests per 5 minutes (free). Pro: 1,000 req/min, 1M calls/mo.
TVL & Protocol Data
# List all protocols with TVL
GET /protocols
# Returns: [{name, slug, tvl, chainTvls, change_1h, change_1d, change_7d, category, chains, ...}]
# Detailed protocol data with historical TVL
GET /protocol/{slug}
# Returns: Full object with tvl[], tokensInUsd{}, currentChainTvls{}, ...
# Simple current TVL number
GET /tvl/{slug}
# Returns: plain number (e.g., 150977324562.40)
# TVL for all chains
GET /v2/chains
# Returns: [{name, tvl, tokenSymbol, chainId, gecko_id}]
# Historical chain TVL
GET /v2/historicalChainTvl/{chain}
# chain: "Ethereum", "Solana", "Arbitrum", etc.
# Returns: [{date, tvl}] — date is unix timestamp (seconds)Token Prices
Coin identifiers use {chain}:{address} format:
solana:So11111111111111111111111111111111111111112(SOL)ethereum:0xdac17f958d2ee523a2206206994597c13d831ec7(USDT)coingecko:bitcoin(non-chain lookups)
# Current prices (batch)
GET /prices/current/{coins}
# coins: comma-separated identifiers
# Optional: searchWidth (default 4h)
# Returns: {coins: {id: {price, decimals, symbol, timestamp, confidence}}}
# Historical price at timestamp
GET /prices/historical/{timestamp}/{coins}
# timestamp: unix seconds
# Price chart
GET /chart/{coins}?period=1d&span=30
# period: 1d, 4h, 1h
# Returns: {coins: {id: {prices: [{timestamp, price}]}}}
# Price change percentage
GET /percentage/{coins}
# First recorded price
GET /prices/first/{coins}
# Block number at timestamp
GET /block/{chain}/{timestamp}Batch Historical Prices
# POST for multiple timestamps per coin
POST /batchHistorical
Body: {"coins": {"solana:So11...": [1709251200, 1709337600]}}DEX Volumes
# All DEXes aggregated
GET /overview/dexs
# Optional: excludeTotalDataChart=true, dataType=dailyVolume
# Chain-specific
GET /overview/dexs/{chain}
# chain: "Solana", "Ethereum", etc.
# Specific DEX
GET /summary/dexs/{protocol}
# Returns: {total24h, total7d, total30d, totalAllTime, totalDataChart, ...}Fees & Revenue
# All protocols
GET /overview/fees
# Optional: dataType=dailyFees|dailyRevenue|dailyUserFees
# Chain-specific
GET /overview/fees/{chain}
# Specific protocol
GET /summary/fees/{protocol}
# Returns: {total24h, total7d, methodology{}, totalDataChart[], ...}Stablecoins
# All stablecoins with supply data
GET https://stablecoins.llama.fi/stablecoins
# Returns: [{name, symbol, pegType, circulating, chainCirculating, price}]
# Historical market cap
GET https://stablecoins.llama.fi/stablecoincharts/all
# Chain-specific stablecoin data
GET https://stablecoins.llama.fi/stablecoincharts/{chain}
# Stablecoin prices (deviation tracking)
GET https://stablecoins.llama.fi/stablecoinpricesBridges
# List all bridges
GET https://bridges.llama.fi/bridges
# Optional: includeChains=true
# Returns: {bridges: [{name, volume stats, chains, ...}]}Common Patterns
Protocol TVL Comparison
def compare_protocol_tvl(slugs: list[str]) -> list[dict]:
"""Compare TVL across protocols."""
results = []
for slug in slugs:
resp = httpx.get(f"https://api.llama.fi/tvl/{slug}", timeout=15.0)
if resp.status_code == 200:
results.append({"protocol": slug, "tvl": resp.json()})
return sorted(results, key=lambda x: x["tvl"], reverse=True)Multi-Token Price Lookup
def get_solana_prices(mints: list[str]) -> dict[str, float]:
"""Get USD prices for Solana tokens via DeFiLlama."""
coins = ",".join(f"solana:{m}" for m in mints)
resp = httpx.get(f"https://coins.llama.fi/prices/current/{coins}")
data = resp.json().get("coins", {})
return {
mint: data[f"solana:{mint}"]["price"]
for mint in mints
if f"solana:{mint}" in data
}Solana DeFi Overview
def solana_defi_snapshot() -> dict:
"""Get a snapshot of Solana DeFi activity."""
chain_tvl = httpx.get("https://api.llama.fi/v2/chains").json()
sol_tvl = next((c["tvl"] for c in chain_tvl if c["name"] == "Solana"), 0)
dex_vol = httpx.get("https://api.llama.fi/overview/dexs/Solana").json()
fees = httpx.get("https://api.llama.fi/overview/fees/Solana").json()
return {
"tvl": sol_tvl,
"dex_volume_24h": dex_vol.get("total24h", 0),
"fees_24h": fees.get("total24h", 0),
}Historical Price Analysis
def price_at_date(coin: str, date_str: str) -> float:
"""Get token price at a specific date.
Args:
coin: DeFiLlama coin ID (e.g., 'solana:So11...')
date_str: Date string 'YYYY-MM-DD'
"""
from datetime import datetime, timezone
dt = datetime.strptime(date_str, "%Y-%m-%d").replace(tzinfo=timezone.utc)
ts = int(dt.timestamp())
resp = httpx.get(f"https://coins.llama.fi/prices/historical/{ts}/{coin}")
data = resp.json().get("coins", {})
return data.get(coin, {}).get("price", 0)Free vs Pro Endpoints
| Category | Free | Pro ($300/mo) |
|---|---|---|
| TVL / Protocols | Yes | Yes |
| Coin Prices | Yes | Yes |
| DEX Volumes (overview) | Yes | Yes |
| Fees/Revenue (overview) | Yes | Yes |
| Stablecoins | Yes | Yes |
| Bridges (list) | Yes | Yes |
| Yields / Pools | No | Yes |
| Bridge detail | No | Yes |
| Derivatives | No | Yes |
| Emissions/Unlocks | No | Yes |
| Treasuries | No | Yes |
| Hacks database | No | Yes |
When to Use DeFiLlama vs Alternatives
| Need | Use |
|---|---|
| Protocol TVL comparison | DeFiLlama |
| Multi-chain token prices | DeFiLlama (free batch) |
| Historical prices at specific timestamps | DeFiLlama |
| DeFi macro analysis | DeFiLlama |
| Solana token OHLCV | Birdeye or SolanaTracker |
| Real-time token data | DexScreener or Birdeye |
| Wallet PnL | SolanaTracker |
| On-chain transaction data | Helius |
Files
References
references/endpoints.md— Complete endpoint listing with parameters and response schemasreferences/coin_identifiers.md— Chain prefixes, address formats, and batch lookup patternsreferences/error_handling.md— Rate limits, error codes, retry strategies, large response handling
Scripts
scripts/defi_snapshot.py— Solana DeFi overview: TVL, volumes, fees, top protocolsscripts/price_lookup.py— Multi-token price lookup with historical comparison
DeFiLlama — Coin Identifier Reference
Format
All coin price endpoints use the format {chain}:{address}.
Supported Chain Prefixes
| Prefix | Chain | Example Address |
|---|---|---|
solana | Solana | So11111111111111111111111111111111111111112 |
ethereum | Ethereum | 0xdAC17F958D2ee523a2206206994597C13D831ec7 |
bsc | BNB Smart Chain | 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c |
polygon | Polygon | 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270 |
arbitrum | Arbitrum | 0x912CE59144191C1204E64559FE8253a0e49E6548 |
optimism | Optimism | 0x4200000000000000000000000000000000000042 |
avalanche | Avalanche | 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7 |
base | Base | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
fantom | Fantom | 0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83 |
coingecko | CoinGecko ID | bitcoin, ethereum, solana |
Common Solana Tokens
SOLANA_COINS = {
"SOL": "solana:So11111111111111111111111111111111111111112",
"USDC": "solana:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"USDT": "solana:Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
"JUP": "solana:JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
"BONK": "solana:DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"WIF": "solana:EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm",
"RAY": "solana:4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R",
"ORCA": "solana:orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE",
}Batch Lookup Patterns
Current Prices (up to 50+ coins)
coins = ",".join(SOLANA_COINS.values())
resp = httpx.get(f"https://coins.llama.fi/prices/current/{coins}")
prices = resp.json()["coins"]
for name, coin_id in SOLANA_COINS.items():
if coin_id in prices:
print(f"{name}: ${prices[coin_id]['price']:.4f}")Historical Comparison
from datetime import datetime, timezone, timedelta
now = int(datetime.now(tz=timezone.utc).timestamp())
week_ago = int((datetime.now(tz=timezone.utc) - timedelta(days=7)).timestamp())
# Current vs 7 days ago
current = httpx.get(f"https://coins.llama.fi/prices/current/{coin}").json()
historical = httpx.get(f"https://coins.llama.fi/prices/historical/{week_ago}/{coin}").json()Using coingecko: prefix for non-chain lookups
For tokens without a specific chain address (BTC, or when you don't know the address):
# Use CoinGecko IDs
resp = httpx.get("https://coins.llama.fi/prices/current/coingecko:bitcoin,coingecko:ethereum,coingecko:solana")Confidence Score
Price responses include a confidence field (0-1):
- 0.99: High confidence, multiple sources agree
- 0.90-0.98: Good confidence
- < 0.90: Low confidence, price may be stale or unreliable
Filter out low-confidence prices for reliable data:
def get_reliable_price(coin: str) -> float | None:
resp = httpx.get(f"https://coins.llama.fi/prices/current/{coin}")
data = resp.json().get("coins", {}).get(coin, {})
if data.get("confidence", 0) < 0.9:
return None
return data.get("price")searchWidth Parameter
Controls how far back to look for a valid price (default: 4h):
# For illiquid tokens, increase searchWidth
resp = httpx.get(f"https://coins.llama.fi/prices/current/{coin}?searchWidth=24h")Values: 4h, 12h, 24h, 48h, 1w
DeFiLlama API — Endpoints Reference
All free endpoints require no authentication. Pro endpoints use key in URL path.
---
TVL & Protocols (https://api.llama.fi)
GET /protocols
All protocols with current TVL. Returns array of objects with: name, slug, tvl, chainTvls, change_1h, change_1d, change_7d, category, chains, symbol, twitter, url.
GET /protocol/{slug}
Detailed protocol data with historical TVL arrays, token breakdowns, chain-specific TVL.
GET /tvl/{slug}
Simple number — current TVL in USD.
GET /v2/chains
All chains with TVL. Returns: [{name, tvl, tokenSymbol, chainId, gecko_id}]
GET /v2/historicalChainTvl
Historical TVL summed across all chains. Returns: [{date, tvl}]
GET /v2/historicalChainTvl/{chain}
Historical TVL for specific chain. Chain names: Ethereum, Solana, Arbitrum, etc.
---
Coin Prices (https://coins.llama.fi)
Coin ID format: {chain}:{address} — e.g., solana:So11111111111111111111111111111111111111112
Supported chains: ethereum, solana, bsc, polygon, arbitrum, optimism, avalanche, base, fantom, coingecko (for non-chain lookups).
GET /prices/current/{coins}
Current prices. Comma-separated coin IDs. Optional: searchWidth (default 4h).
{"coins": {"solana:So11...": {"price": 147.23, "decimals": 9, "symbol": "SOL", "timestamp": 1709251200, "confidence": 0.99}}}GET /prices/historical/{timestamp}/{coins}
Prices at unix timestamp. Same response schema.
POST /batchHistorical
Batch multiple timestamps: {"coins": {"solana:So11...": [ts1, ts2]}}
GET /chart/{coins}
Price chart. Query params: period (1d, 4h, 1h), span (number of periods).
GET /percentage/{coins}
Price change percentage. Optional: timestamp, lookForward.
GET /prices/first/{coins}
First recorded price for tokens.
GET /block/{chain}/{timestamp}
Block number closest to timestamp.
---
DEX Volumes (https://api.llama.fi)
GET /overview/dexs
Aggregated DEX volumes. Optional: excludeTotalDataChart=true, dataType=dailyVolume.
GET /overview/dexs/{chain}
Chain-specific DEX volumes.
GET /summary/dexs/{protocol}
Specific DEX detail: total24h, total7d, total30d, totalAllTime, totalDataChart[].
---
Fees & Revenue (https://api.llama.fi)
GET /overview/fees
All protocol fees. Optional: dataType = dailyFees, dailyRevenue, dailyUserFees.
GET /overview/fees/{chain}
Chain-specific fees.
GET /summary/fees/{protocol}
Specific protocol with methodology: {total24h, methodology{UserFees, Fees, Revenue, ProtocolRevenue}, totalDataChart[]}.
---
Stablecoins (https://stablecoins.llama.fi)
GET /stablecoins
All stablecoins: {name, symbol, pegType, circulating, chainCirculating, price}.
GET /stablecoincharts/all
Historical aggregated stablecoin market cap.
GET /stablecoincharts/{chain}
Chain-specific. Optional: stablecoin (filter by stablecoin ID).
GET /stablecoinprices
Historical stablecoin price deviations.
GET /stablecoinchains
All chains with stablecoin data.
---
Bridges (https://bridges.llama.fi)
GET /bridges
List all bridges. Optional: includeChains=true. Returns: {bridges: [{name, last24hVolume, weeklyVolume, monthlyVolume, chains}]}
---
Options (https://api.llama.fi)
GET /overview/options
Options trading volumes. Optional: excludeTotalDataChart.
GET /overview/options/{chain}
Chain-specific options.
GET /summary/options/{protocol}
Specific protocol detail.
---
Pro-Only Endpoints
All require API key in URL path: https://pro-api.llama.fi/{KEY}/api/...
| Endpoint | Description |
|---|---|
/pools | All yield pools with APY |
/chart/{pool} | Historical APY/TVL for pool |
/poolsBorrow | Borrowing rates |
/perps | Perpetual funding rates |
/lsdRates | Liquid staking rates |
/bridge/{id} | Bridge detail |
/bridgevolume/{chain} | Bridge volume by chain |
/api/categories | TVL by category |
/api/treasuries | Protocol treasuries |
/api/hacks | Exploit database |
/api/raises | Funding rounds |
/api/emissions | Token unlock schedules |
/api/emission/{protocol} | Specific vesting |
/etfs/overview | Crypto ETF data |
/usage/APIKEY | API usage stats |
---
Response Size Warning
/protocols, /overview/dexs, and /overview/fees return very large payloads (10MB+). Always use excludeTotalDataChart=true and excludeTotalDataChartBreakdown=true to reduce response size.
DeFiLlama API — Error Handling
Rate Limits
| Tier | Limit | Monthly |
|---|---|---|
| Free | ~500 req / 5 min | Unlimited |
| API ($300/mo) | 1,000 req/min | 1M calls |
No rate limit headers are returned. HTTP 429 means you're rate limited.
HTTP Status Codes
| Code | Meaning | Action |
|---|---|---|
| 200 | Success | Process response |
| 400 | Bad request | Check coin IDs, parameters |
| 429 | Rate limited | Wait 30-60 seconds |
| 502 | Upstream error | Retry after 5 seconds |
| 504 | Gateway timeout | Response too large, use exclude params |
Retry Strategy
import time
import httpx
def llama_get(url: str, max_retries: int = 3) -> dict | list:
"""GET with retry for DeFiLlama endpoints."""
for attempt in range(max_retries):
try:
resp = httpx.get(url, timeout=30.0)
if resp.status_code == 200:
return resp.json()
if resp.status_code == 429:
wait = 30.0 * (attempt + 1)
print(f"Rate limited, waiting {wait}s...")
time.sleep(wait)
continue
if resp.status_code >= 500:
time.sleep(5.0 * (attempt + 1))
continue
resp.raise_for_status()
except httpx.TimeoutException:
if attempt < max_retries - 1:
time.sleep(5.0)
continue
raise
raise RuntimeError(f"Failed after {max_retries} retries: {url}")Common Issues
1. Large response payloads
/protocols and volume/fee overviews can return 10MB+. Always use filter params:
resp = httpx.get("https://api.llama.fi/overview/dexs", params={
"excludeTotalDataChart": "true",
"excludeTotalDataChartBreakdown": "true",
})2. Timestamps are seconds, not milliseconds
All DeFiLlama timestamps are unix seconds. Don't multiply by 1000.
3. Missing coin data
If a coin ID returns no data, check:
- Address format (lowercase for EVM, base58 for Solana)
- Chain prefix is correct
- Token has on-chain liquidity
- Try
searchWidth=24hfor illiquid tokens
4. Stale prices
Check the timestamp and confidence fields:
import time
data = resp.json()["coins"].get(coin_id, {})
age = time.time() - data.get("timestamp", 0)
if age > 3600: # older than 1 hour
print("Warning: stale price data")
if data.get("confidence", 0) < 0.9:
print("Warning: low confidence price")5. Protocol slugs
Protocol slugs don't always match the display name:
- "Raydium" →
raydium - "Aave V3" →
aave-v3 - "Uniswap V3" →
uniswap
Use /protocols to find the correct slug field.
6. Chain name casing
Chain names in URLs are case-sensitive:
- Correct:
Solana,Ethereum,Arbitrum - Wrong:
solana,SOLANA
This applies to /overview/dexs/{chain}, /overview/fees/{chain}, /v2/historicalChainTvl/{chain}.
Caching Recommendations
| Data | Cache TTL | Reason |
|---|---|---|
| Protocol list | 15-30 min | Changes slowly |
| TVL (current) | 5-15 min | Updates periodically |
| Historical TVL | 1 hour+ | Immutable history |
| Prices (current) | 1-5 min | Changes constantly |
| Prices (historical) | Forever | Immutable |
| Volume/Fee overviews | 15-30 min | Daily aggregation |
#!/usr/bin/env python3
"""Solana DeFi overview using DeFiLlama (free, no auth).
Fetches TVL, DEX volumes, fees, and top protocols for Solana.
Produces a comprehensive DeFi macro snapshot.
Usage:
python scripts/defi_snapshot.py
CHAIN="Ethereum" python scripts/defi_snapshot.py
Dependencies:
uv pip install httpx
"""
import os
import sys
import time
from typing import Optional
import httpx
# ── Configuration ───────────────────────────────────────────────────
CHAIN = os.getenv("CHAIN", "Solana")
BASE = "https://api.llama.fi"
COINS = "https://coins.llama.fi"
# ── API Helper ──────────────────────────────────────────────────────
def llama_get(url: str, params: Optional[dict] = None) -> dict | list:
"""GET request to DeFiLlama with retry.
Args:
url: Full URL.
params: Query parameters.
Returns:
Parsed JSON response.
"""
for attempt in range(3):
try:
resp = httpx.get(url, params=params or {}, timeout=30.0)
if resp.status_code == 429:
time.sleep(30.0)
continue
if resp.status_code >= 500:
time.sleep(5.0)
continue
resp.raise_for_status()
return resp.json()
except httpx.TimeoutException:
if attempt < 2:
time.sleep(5.0)
continue
raise
return {}
# ── Data Fetching ───────────────────────────────────────────────────
def get_chain_tvl(chain: str) -> float:
"""Get current TVL for a chain.
Args:
chain: Chain name (e.g., 'Solana').
Returns:
TVL in USD.
"""
chains = llama_get(f"{BASE}/v2/chains")
if not isinstance(chains, list):
return 0
for c in chains:
if c.get("name", "").lower() == chain.lower():
return c.get("tvl", 0)
return 0
def get_top_protocols(chain: str, limit: int = 15) -> list[dict]:
"""Get top protocols by TVL for a chain.
Args:
chain: Chain name.
limit: Number of protocols to return.
Returns:
Sorted list of protocol dicts.
"""
protocols = llama_get(f"{BASE}/protocols")
if not isinstance(protocols, list):
return []
chain_protocols = []
for p in protocols:
chains = p.get("chains", [])
if chain in chains:
chain_tvl = p.get("chainTvls", {}).get(chain, 0)
if chain_tvl > 0:
chain_protocols.append({
"name": p.get("name", "?"),
"category": p.get("category", "?"),
"tvl": chain_tvl,
"change_1d": p.get("change_1d", 0),
"change_7d": p.get("change_7d", 0),
})
chain_protocols.sort(key=lambda x: x["tvl"], reverse=True)
return chain_protocols[:limit]
def get_dex_volumes(chain: str) -> dict:
"""Get DEX volume overview for a chain.
Args:
chain: Chain name.
Returns:
Volume summary dict.
"""
data = llama_get(f"{BASE}/overview/dexs/{chain}", {
"excludeTotalDataChart": "true",
"excludeTotalDataChartBreakdown": "true",
})
if not isinstance(data, dict):
return {}
return data
def get_fees(chain: str) -> dict:
"""Get fee overview for a chain.
Args:
chain: Chain name.
Returns:
Fee summary dict.
"""
data = llama_get(f"{BASE}/overview/fees/{chain}", {
"excludeTotalDataChart": "true",
"excludeTotalDataChartBreakdown": "true",
})
if not isinstance(data, dict):
return {}
return data
def get_stablecoin_supply(chain: str) -> dict:
"""Get stablecoin supply for a chain.
Args:
chain: Chain name.
Returns:
Stablecoin summary dict.
"""
stables = llama_get("https://stablecoins.llama.fi/stablecoins")
if not isinstance(stables, list):
return {}
total = 0
breakdown = {}
for s in stables:
chain_data = s.get("chainCirculating", {}).get(chain, {})
current = chain_data.get("current", {}).get("peggedUSD", 0)
if current > 0:
total += current
breakdown[s["symbol"]] = current
return {"total": total, "breakdown": breakdown}
# ── Display ─────────────────────────────────────────────────────────
def format_usd(value: float) -> str:
"""Format USD value for display."""
if not value:
return "$0"
if abs(value) >= 1e9:
return f"${value / 1e9:.2f}B"
elif abs(value) >= 1e6:
return f"${value / 1e6:.2f}M"
elif abs(value) >= 1e3:
return f"${value / 1e3:.1f}K"
return f"${value:.2f}"
def print_report(
chain: str,
tvl: float,
protocols: list[dict],
volumes: dict,
fees: dict,
stables: dict,
) -> None:
"""Print DeFi snapshot report."""
print(f"\n{'='*60}")
print(f"DeFi SNAPSHOT — {chain}")
print(f"{'='*60}")
# Overview
print(f"\n--- Overview ---")
print(f" Total TVL: {format_usd(tvl)}")
vol_24h = volumes.get("total24h", 0)
print(f" DEX Volume 24h: {format_usd(vol_24h)}")
fees_24h = fees.get("total24h", 0)
print(f" Fees 24h: {format_usd(fees_24h)}")
print(f" Stablecoin Supply:{format_usd(stables.get('total', 0))}")
# Top protocols
if protocols:
print(f"\n--- Top Protocols by TVL ---")
print(f" {'Protocol':<25} {'Category':<15} {'TVL':>12} {'1d%':>7} {'7d%':>7}")
print(f" {'─'*25} {'─'*15} {'─'*12} {'─'*7} {'─'*7}")
for p in protocols:
d1 = p.get("change_1d", 0) or 0
d7 = p.get("change_7d", 0) or 0
print(f" {p['name']:<25} {p['category']:<15} "
f"{format_usd(p['tvl']):>12} {d1:>+6.1f}% {d7:>+6.1f}%")
# Top DEXes by volume
dex_protocols = volumes.get("protocols", [])
if dex_protocols:
top_dexes = sorted(dex_protocols, key=lambda x: x.get("total24h", 0) or 0, reverse=True)[:10]
print(f"\n--- Top DEXes by Volume ---")
print(f" {'DEX':<20} {'Vol 24h':>14} {'Vol 7d':>14}")
print(f" {'─'*20} {'─'*14} {'─'*14}")
for d in top_dexes:
v24 = d.get("total24h", 0) or 0
v7d = d.get("total7d", 0) or 0
if v24 > 0:
print(f" {d.get('name', '?'):<20} {format_usd(v24):>14} {format_usd(v7d):>14}")
# Stablecoin breakdown
breakdown = stables.get("breakdown", {})
if breakdown:
print(f"\n--- Stablecoin Supply ---")
sorted_stables = sorted(breakdown.items(), key=lambda x: x[1], reverse=True)
for symbol, supply in sorted_stables[:5]:
pct = supply / stables["total"] * 100 if stables["total"] > 0 else 0
print(f" {symbol:<8} {format_usd(supply):>14} ({pct:.1f}%)")
print()
# ── Main ────────────────────────────────────────────────────────────
def main() -> None:
"""Run DeFi snapshot."""
print(f"Generating DeFi snapshot for {CHAIN}...")
print("Fetching TVL...")
tvl = get_chain_tvl(CHAIN)
time.sleep(0.5)
print("Fetching top protocols...")
protocols = get_top_protocols(CHAIN)
time.sleep(0.5)
print("Fetching DEX volumes...")
volumes = get_dex_volumes(CHAIN)
time.sleep(0.5)
print("Fetching fees...")
fees = get_fees(CHAIN)
time.sleep(0.5)
print("Fetching stablecoin data...")
stables = get_stablecoin_supply(CHAIN)
print_report(CHAIN, tvl, protocols, volumes, fees, stables)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Multi-token price lookup with historical comparison via DeFiLlama.
Fetches current prices for multiple tokens and compares against
historical prices. Free, no authentication required.
Usage:
python scripts/price_lookup.py
TOKENS="SOL,JUP,BONK" python scripts/price_lookup.py
Dependencies:
uv pip install httpx
"""
import os
import sys
import time
from datetime import datetime, timezone, timedelta
from typing import Optional
import httpx
# ── Configuration ───────────────────────────────────────────────────
COINS_BASE = "https://coins.llama.fi"
# Well-known Solana tokens
TOKEN_MAP = {
"SOL": "solana:So11111111111111111111111111111111111111112",
"USDC": "solana:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"USDT": "solana:Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
"JUP": "solana:JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
"BONK": "solana:DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"WIF": "solana:EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm",
"RAY": "solana:4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R",
"ORCA": "solana:orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE",
"BTC": "coingecko:bitcoin",
"ETH": "coingecko:ethereum",
}
TOKENS_INPUT = os.getenv("TOKENS", "SOL,JUP,BONK,WIF,RAY")
SELECTED_TOKENS = [t.strip().upper() for t in TOKENS_INPUT.split(",")]
# ── API Helper ──────────────────────────────────────────────────────
def llama_get(url: str, max_retries: int = 3) -> dict:
"""GET request to DeFiLlama with retry."""
for attempt in range(max_retries):
try:
resp = httpx.get(url, timeout=30.0)
if resp.status_code == 200:
return resp.json()
if resp.status_code == 429:
time.sleep(30.0)
continue
if resp.status_code >= 500:
time.sleep(5.0)
continue
resp.raise_for_status()
except httpx.TimeoutException:
if attempt < max_retries - 1:
time.sleep(5.0)
continue
raise
return {}
# ── Price Functions ─────────────────────────────────────────────────
def get_current_prices(coin_ids: list[str]) -> dict[str, dict]:
"""Get current prices for multiple coins.
Args:
coin_ids: List of DeFiLlama coin identifiers.
Returns:
Dict of coin_id -> {price, symbol, confidence, timestamp}.
"""
joined = ",".join(coin_ids)
data = llama_get(f"{COINS_BASE}/prices/current/{joined}")
return data.get("coins", {})
def get_historical_prices(coin_ids: list[str], timestamp: int) -> dict[str, dict]:
"""Get prices at a historical timestamp.
Args:
coin_ids: List of DeFiLlama coin identifiers.
timestamp: Unix timestamp (seconds).
Returns:
Dict of coin_id -> {price, symbol, confidence, timestamp}.
"""
joined = ",".join(coin_ids)
data = llama_get(f"{COINS_BASE}/prices/historical/{timestamp}/{joined}?searchWidth=12h")
return data.get("coins", {})
def get_price_changes(coin_ids: list[str]) -> dict[str, float]:
"""Get percentage price changes.
Args:
coin_ids: List of DeFiLlama coin identifiers.
Returns:
Dict of coin_id -> percentage change.
"""
joined = ",".join(coin_ids)
data = llama_get(f"{COINS_BASE}/percentage/{joined}")
return data.get("coins", {})
# ── Display ─────────────────────────────────────────────────────────
def print_report(
selected: list[str],
current: dict[str, dict],
prices_7d: dict[str, dict],
prices_30d: dict[str, dict],
) -> None:
"""Print price comparison report.
Args:
selected: List of token symbols.
current: Current price data.
prices_7d: 7-day ago prices.
prices_30d: 30-day ago prices.
"""
print(f"\n{'='*75}")
print(f"TOKEN PRICE COMPARISON")
print(f"{'='*75}")
print(f" {'Token':<8} {'Price':>14} {'7d Change':>12} {'30d Change':>12} {'Confidence':>12}")
print(f" {'─'*8} {'─'*14} {'─'*12} {'─'*12} {'─'*12}")
for symbol in selected:
coin_id = TOKEN_MAP.get(symbol)
if not coin_id or coin_id not in current:
print(f" {symbol:<8} {'N/A':>14}")
continue
price = current[coin_id].get("price", 0)
confidence = current[coin_id].get("confidence", 0)
# 7d change
price_7d = prices_7d.get(coin_id, {}).get("price", 0)
change_7d = ((price / price_7d) - 1) * 100 if price_7d > 0 else 0
# 30d change
price_30d = prices_30d.get(coin_id, {}).get("price", 0)
change_30d = ((price / price_30d) - 1) * 100 if price_30d > 0 else 0
# Format price
if price >= 100:
price_str = f"${price:,.2f}"
elif price >= 1:
price_str = f"${price:.4f}"
elif price >= 0.001:
price_str = f"${price:.6f}"
else:
price_str = f"${price:.10f}"
change_7d_str = f"{change_7d:+.1f}%" if price_7d > 0 else "N/A"
change_30d_str = f"{change_30d:+.1f}%" if price_30d > 0 else "N/A"
conf_str = f"{confidence:.2f}"
print(f" {symbol:<8} {price_str:>14} {change_7d_str:>12} {change_30d_str:>12} {conf_str:>12}")
# Best/worst performers
changes = {}
for symbol in selected:
coin_id = TOKEN_MAP.get(symbol)
if not coin_id:
continue
price = current.get(coin_id, {}).get("price", 0)
price_7d = prices_7d.get(coin_id, {}).get("price", 0)
if price > 0 and price_7d > 0:
changes[symbol] = ((price / price_7d) - 1) * 100
if changes:
best = max(changes, key=changes.get)
worst = min(changes, key=changes.get)
print(f"\n 7d Best: {best} ({changes[best]:+.1f}%)")
print(f" 7d Worst: {worst} ({changes[worst]:+.1f}%)")
print()
# ── Main ────────────────────────────────────────────────────────────
def main() -> None:
"""Run price lookup and comparison."""
# Resolve coin IDs
coin_ids = []
valid_tokens = []
for symbol in SELECTED_TOKENS:
if symbol in TOKEN_MAP:
coin_ids.append(TOKEN_MAP[symbol])
valid_tokens.append(symbol)
elif symbol.startswith("solana:") or symbol.startswith("coingecko:"):
coin_ids.append(symbol)
valid_tokens.append(symbol)
else:
print(f" Unknown token: {symbol} (use symbol name or full coin ID)")
if not coin_ids:
print("No valid tokens specified.")
sys.exit(1)
print(f"Looking up {len(coin_ids)} tokens...")
# Timestamps
now = datetime.now(tz=timezone.utc)
ts_7d = int((now - timedelta(days=7)).timestamp())
ts_30d = int((now - timedelta(days=30)).timestamp())
# Fetch current and historical prices
print("Fetching current prices...")
current = get_current_prices(coin_ids)
time.sleep(0.5)
print("Fetching 7-day ago prices...")
prices_7d = get_historical_prices(coin_ids, ts_7d)
time.sleep(0.5)
print("Fetching 30-day ago prices...")
prices_30d = get_historical_prices(coin_ids, ts_30d)
print_report(valid_tokens, current, prices_7d, prices_30d)
if __name__ == "__main__":
main()
Related skills
FAQ
Does DeFiLlama require an API key?
No. It is free with no authentication for most endpoints; yields and bridge detail require a Pro key.
How are tokens identified?
Coin identifiers use the {chain}:{address} format, e.g. solana:So111...112 for SOL.