Crypto Fear & Greed Historical API: A Trader's Guide
Learn how to access crypto fear & greed historical data via API, parse responses, and build trading signals with real Python and JavaScript code examples.
Learn how to access crypto fear & greed historical data via API, parse responses, and build trading signals with real Python and JavaScript code examples.
Market sentiment is one of the most underused edges in crypto trading. While most traders are glued to price charts, the pros are quietly pulling historical fear and greed data to understand the emotional cycles that drive those very charts. The Crypto Fear & Greed Index — originally popularized by Alternative.me — compresses dozens of market signals into a single number from 0 (Extreme Fear) to 100 (Extreme Greed). The historical version of this index via API is where the real alpha lives.
Before hitting any endpoint, it helps to know what you're actually querying. The index is a composite score built from several weighted components: volatility accounts for roughly 25% of the score, market momentum and volume together add another 25%, social media sentiment (primarily Twitter/X and Reddit) contributes 15%, Bitcoin dominance another 10%, and Google Trends data rounds out the remaining 10-15%. The specific weights shift slightly depending on the data provider.
When the index sits below 25, markets are in Extreme Fear — historically a contrarian buy zone. Above 75 signals Extreme Greed, which has consistently preceded sharp corrections. The historical API lets you map these thresholds against price action retroactively, giving you a calibration baseline that pure TA simply cannot provide.
The Alternative.me Fear & Greed API is free, requires no authentication for basic access, and returns clean JSON. The base endpoint is straightforward, and the limit parameter is what unlocks historical data. By default it returns the latest value. Bump limit to 365 and you get a full year of daily readings.
import requests
import pandas as pd
from datetime import datetime
def fetch_fear_greed_history(limit: int = 365) -> pd.DataFrame:
"""
Fetch historical Fear & Greed index data.
limit: number of days to retrieve (max ~2000 for free tier)
"""
url = "https://api.alternative.me/fng/"
params = {
"limit": limit,
"format": "json",
"date_format": "us" # returns MM/DD/YYYY format
}
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
except requests.exceptions.Timeout:
print("Request timed out — API may be slow, retry in 30s")
return pd.DataFrame()
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e}")
return pd.DataFrame()
records = data.get("data", [])
df = pd.DataFrame(records)
# Convert types
df["value"] = pd.to_numeric(df["value"])
df["timestamp"] = pd.to_datetime(df["timestamp"].astype(int), unit="s")
df = df.sort_values("timestamp").reset_index(drop=True)
return df
# Usage
df = fetch_fear_greed_history(limit=365)
print(df.tail(10))
print(f"\nDate range: {df['timestamp'].min().date()} → {df['timestamp'].max().date()}")
print(f"Average sentiment: {df['value'].mean():.1f}")
The response includes a timestamp (Unix epoch), the numeric value, the classification label (e.g. 'Fear', 'Greed'), and a time_until_update field indicating when the next value publishes. For historical analysis you only need timestamp and value — the label is useful for display but is just a bucketed version of the numeric score.
Raw sentiment data only becomes actionable when you layer it against price. On Binance you can pull daily OHLCV via their public REST API without authentication, making it a natural pairing with the fear/greed feed. The merge key is the UTC date — both APIs produce one record per day.
import requests
import pandas as pd
def fetch_binance_daily(symbol: str = "BTCUSDT", days: int = 365) -> pd.DataFrame:
"""Fetch daily OHLCV from Binance public API — no auth needed."""
url = "https://api.binance.com/api/v3/klines"
params = {
"symbol": symbol,
"interval": "1d",
"limit": days
}
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status()
raw = resp.json()
df = pd.DataFrame(raw, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_volume", "trades",
"taker_buy_base", "taker_buy_quote", "ignore"
])
df["date"] = pd.to_datetime(df["open_time"], unit="ms").dt.date
df["close"] = pd.to_numeric(df["close"])
return df[["date", "close", "volume"]]
def merge_sentiment_price(days: int = 365) -> pd.DataFrame:
"""Merge BTC price data with Fear & Greed history."""
# Fetch both datasets
fg_df = fetch_fear_greed_history(limit=days)
fg_df["date"] = fg_df["timestamp"].dt.date
price_df = fetch_binance_daily(days=days)
# Merge on date
merged = pd.merge(price_df, fg_df[["date", "value", "value_classification"]],
on="date", how="inner")
merged = merged.rename(columns={"value": "fg_score",
"value_classification": "sentiment"})
# Add next-day return for backtesting signal quality
merged["next_day_return"] = merged["close"].pct_change(1).shift(-1) * 100
return merged
df = merge_sentiment_price(365)
# Simple stat: what was avg next-day return when index < 25?
extreme_fear = df[df["fg_score"] < 25]
print(f"Extreme Fear days: {len(extreme_fear)}")
print(f"Avg next-day return on extreme fear days: {extreme_fear['next_day_return'].mean():.2f}%")
print(f"Win rate (positive next day): {(extreme_fear['next_day_return'] > 0).mean() * 100:.1f}%")
Important: the Alternative.me API updates once per day around midnight UTC. If you're hitting the endpoint multiple times intraday, cache the response locally — hammering it achieves nothing and risks getting rate-limited.
If you're building a web dashboard or a Node.js bot that watches sentiment shifts — say, for posting alerts when the index crosses into Extreme Fear — JavaScript works equally well. Platforms like Bybit and OKX both offer WebSocket APIs for real-time price feeds, so you can combine live price action with scheduled sentiment polls.
const axios = require('axios');
const THRESHOLDS = {
EXTREME_FEAR: 25,
FEAR: 49,
GREED: 74,
EXTREME_GREED: 100
};
async function fetchFearGreedHistory(limit = 30) {
try {
const response = await axios.get('https://api.alternative.me/fng/', {
params: { limit, format: 'json' },
timeout: 8000
});
const data = response.data.data;
return data.map(entry => ({
date: new Date(parseInt(entry.timestamp) * 1000).toISOString().split('T')[0],
score: parseInt(entry.value),
label: entry.value_classification
})).reverse(); // oldest first
} catch (error) {
if (error.code === 'ECONNABORTED') {
console.error('Request timed out — check network or API status');
} else {
console.error(`API error: ${error.message}`);
}
return [];
}
}
function classifySignal(score) {
if (score <= THRESHOLDS.EXTREME_FEAR) return { signal: 'STRONG_BUY', reason: 'Extreme Fear — contrarian accumulation zone' };
if (score <= THRESHOLDS.FEAR) return { signal: 'BUY', reason: 'Fear — market undervalued by sentiment' };
if (score <= THRESHOLDS.GREED) return { signal: 'HOLD', reason: 'Neutral to bullish sentiment' };
return { signal: 'CAUTION', reason: 'Extreme Greed — consider reducing exposure' };
}
async function runSentimentMonitor() {
const history = await fetchFearGreedHistory(7);
if (!history.length) return;
const latest = history[history.length - 1];
const { signal, reason } = classifySignal(latest.score);
console.log(`\n=== Fear & Greed Sentiment Monitor ===`);
console.log(`Date: ${latest.date}`);
console.log(`Score: ${latest.score} (${latest.label})`);
console.log(`Signal: ${signal}`);
console.log(`Reason: ${reason}`);
// Show 7-day trend
console.log(`\n7-Day Trend:`);
history.forEach(d => {
const bar = '█'.repeat(Math.floor(d.score / 5));
console.log(` ${d.date} ${String(d.score).padStart(3)} ${bar}`);
});
}
runSentimentMonitor();
Historical data is only valuable if it informs rules you can actually execute. Here are three approaches that have held up in backtests across multiple market cycles.
The simplest is the contrarian DCA trigger: set an automated buy order on Coinbase or Binance whenever the index drops below 20 for two consecutive days. This filters out single-day fear spikes (which can be noise) and catches genuine capitulation events. Historical data from 2019 through 2024 shows this rule caught the bottoms of the March 2020 crash, the May 2021 correction, and the June 2022 capitulation within days.
A more sophisticated approach is the sentiment-momentum divergence trade. When price is making higher lows but sentiment is still falling (declining fear/greed score despite rising price), it signals that the crowd hasn't yet recognized the recovery — historically a strong setup on assets like BTC and ETH. Tools like VoiceOfChain surface this kind of signal in real time, combining on-chain flow data with sentiment overlays so you're not manually cross-referencing spreadsheets.
For derivatives traders on OKX or Bybit, the index also provides useful context for funding rate interpretation. When funding rates are deeply negative AND the fear index is below 20, the double-negative tends to resolve sharply to the upside — short squeeze territory. Pulling 90 days of historical fear/greed and overlaying it against historical funding rates (available via both OKX and Bybit's REST APIs) gives you a powerful composite signal.
| Zone | Score Range | Historical Bias | Position Suggestion |
|---|---|---|---|
| Extreme Fear | 0–24 | Strong contrarian buy | Accumulate spot, reduce shorts |
| Fear | 25–49 | Mild contrarian buy | Scale in, hedge optional |
| Greed | 50–74 | Neutral/cautious | Hold, tighten stops |
| Extreme Greed | 75–100 | Contrarian sell | Take profits, reduce longs |
The free Alternative.me API doesn't publish explicit rate limits but practically speaking it tolerates a few hundred requests per day without issue. For production systems, the right approach is to fetch once per day post-midnight UTC, store the result locally (SQLite or Postgres), and serve your application from the local database. Never poll the API in real time — the index updates daily, so intraday polling wastes quota and adds latency for no gain.
If you need intraday granularity or a more robust data feed, several professional data vendors (including Kaiko and Messari) offer sentiment indices with hourly resolution, though those require paid API keys. For most retail trading strategies, daily resolution is sufficient and the free endpoint handles it reliably.
Pro tip: always store raw API responses with a fetched_at timestamp. APIs change their historical data occasionally (methodology updates, data corrections). Having your own immutable archive protects your backtests from retroactive changes.
For teams building automated signals into platforms like VoiceOfChain, the architecture pattern that works well is: a lightweight daily cron job fetches and persists fear/greed data into ClickHouse or Postgres, and a separate signal engine queries that local store. This decouples your signal logic from API availability and lets you run backtests against years of history without hitting any rate limits.
The crypto fear and greed historical API is one of the most accessible and genuinely useful free data sources available to retail traders. A few dozen lines of Python or JavaScript gets you years of sentiment history that you can overlay against price, combine with on-chain metrics, or wire into automated trading systems. The traders using this data aren't doing anything exotic — they're simply making decisions with more context than the crowd has. Start with the basic fetch, merge it against Binance daily candles, and see for yourself how often sentiment extremes align with price turning points. The edge is there in the data. The work is in building the discipline to act on it.