What data does a crypto trading agent actually need?
Most agent builds start and stall in the same place: the agent can fetch a price, so it recites prices. Ask it whether a setup is crowded, what a position costs to hold, or whether a rally has fresh money behind it, and it has nothing to work with.
A crypto market data API for a trading agent needs to answer three separate questions, and price answers none of them:
- Candles — where has price been, and what did participation look like? This is your entire technical surface: trend, volatility, ranges, volume.
- Funding — what does holding this position cost, and which side is paying? This is sentiment expressed in money rather than opinion.
- Open interest — how much leveraged size is in the trade? This tells you whether a move has fresh capital behind it or is just people closing out.
Together they cover direction, cost and crowding. Missing any one produces a specific, predictable failure: no candles and the agent has no context; no funding and it ignores holding cost until it bleeds out; no open interest and it cannot tell a breakout from a short squeeze.
The rest of this post is the wiring: three endpoints, working tool definitions, the request budget, and the traps that make each one silently wrong.
The three calls, end to end
Every endpoint below is one authenticated GET against https://cryptodataapi.com/api/v1 with an X-API-Key header. No SDK, no websocket, no state.
1. Candles — hourly OHLCV with order-flow fields:
curl -H "X-API-Key: $KEY" \
"https://cryptodataapi.com/api/v1/market-data/klines?symbol=BTCUSDT&interval=1h&limit=200"Returns named fields per bar — open_time, OHLC, volume, quote_volume, trades, and taker-buy volumes so you get aggressor split without a second request.
2. Funding — both venues, one call:
curl -H "X-API-Key: $KEY" \
"https://cryptodataapi.com/api/v1/derivatives/funding-rates?coin=BTC"3. Open interest — both venues, one call:
curl -H "X-API-Key: $KEY" \
"https://cryptodataapi.com/api/v1/derivatives/open-interest?coin=BTC"Calls 2 and 3 collapse into one if you want the whole derivatives picture — funding, OI and the long/short account split:
curl -H "X-API-Key: $KEY" \
"https://cryptodataapi.com/api/v1/derivatives/summary?coin=BTC"{
"coin": "BTC",
"binance": {
"funding": { "current_rate": 0.00005927, "avg_rate": 0.000077917 },
"open_interest": { "current": 105453.212, "trend_30d_pct": 0.94 },
"long_short": { "ratio": 1.0008, "top_trader_ratio": 1.089 }
},
"hyperliquid": {
"funding_rate": 0.0000125,
"open_interest_usd": 2775642098.16005,
"mark_price": 78242.5,
"premium": -0.0003104864
}
}So the real minimum is two requests per symbol: one for candles, one for the derivatives summary. That is the whole spine.
Wiring the three as function-calling tools
Agents do not consume endpoints, they consume tool definitions. Keep the surface small and the descriptions specific — a model picks the wrong tool when two descriptions overlap:
import requests
BASE = "https://cryptodataapi.com/api/v1"
H = {"X-API-Key": "cdk_live_YOUR_KEY"}
TOOLS = [
{
"name": "get_candles",
"description": (
"Hourly OHLCV price history for a Binance spot pair. Use for trend, "
"volatility, support/resistance and any technical indicator. "
"Returns up to 1000 bars."
),
"input_schema": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "e.g. BTCUSDT"},
"interval": {"type": "string", "enum": ["1h", "4h", "1d"]},
"limit": {"type": "integer", "maximum": 1000},
},
"required": ["symbol"],
},
},
{
"name": "get_derivatives",
"description": (
"Funding rate, open interest and long/short ratio for a coin across "
"Binance and Hyperliquid. Use to judge positioning, crowding and the "
"cost of holding a leveraged position. NOT for price history."
),
"input_schema": {
"type": "object",
"properties": {
"coin": {"type": "string", "description": "Bare symbol, e.g. BTC"},
},
"required": ["coin"],
},
},
]
def run_tool(name, args):
if name == "get_candles":
args.setdefault("interval", "1h")
args.setdefault("limit", 200)
return requests.get(f"{BASE}/market-data/klines",
params=args, headers=H, timeout=15).json()
if name == "get_derivatives":
return requests.get(f"{BASE}/derivatives/summary",
params=args, headers=H, timeout=15).json()
raise ValueError(name)Two details that matter more than they look. The NOT for price history clause in the second description measurably reduces mis-selection — telling a model what a tool is not for is as useful as telling it what it is for. And note the parameter asymmetry: symbol=BTCUSDT for candles, coin=BTC for derivatives. Encode that in the schema descriptions or the model will guess, and guess wrong.
Or skip the wiring entirely with MCP
If your agent speaks Model Context Protocol — Claude Code, Claude Desktop, Cursor and a growing list of others — there is nothing to write. One command registers 26 tools, including get_funding_rates, get_open_interest and get_order_book:
export CRYPTODATA_API_KEY=cdk_live_YOUR_KEY
claude mcp add --transport http cryptodataapi \
https://cryptodataapi.com/mcp \
--header 'X-API-Key: ${CRYPTODATA_API_KEY}'The single-quoted ${CRYPTODATA_API_KEY} is deliberate — it expands at connect time, so the literal key never lands in a config file or a conversation log.
Two things worth knowing about the tool set:
- There is no dedicated candles tool. Candles come through
query_api, a general escape hatch that takes any/api/v1path and params — so an agent can reach/market-data/klineswithout a bespoke wrapper. - The agent can mint its own key.
create_free_api_keyworks without credentials, so an agent evaluating the API can bootstrap itself instead of stopping to ask a human for a secret.
For non-MCP stacks, everything above is a plain REST call and works from any language that can send an HTTP header.
Three traps that make agent data silently wrong
Each of the three signals has a failure mode that produces plausible numbers rather than an error. Those are the expensive ones.
| Signal | The trap | The fix |
|---|---|---|
| Candles | The newest bar is still open — its close and volume are partial and will change | Drop rows where close_time >= now_ms; request limit=N+1 to keep N settled bars |
| Funding | Rates are quoted per settlement interval — 8h on Binance, 1h on Hyperliquid | Annualize before comparing: rate * 1095 for 8h, rate * 8760 for 1h |
| Open interest | current_oi is in base units, not dollars | Multiply by mark price, or read open_interest_usd / sum_open_interest_value |
The funding one is worth a concrete illustration because the error inverts the answer. Today Binance quoted BTC funding at 0.00005927 and Hyperliquid at 0.0000125. Hyperliquid's number is 4.7× smaller. Annualized, Binance runs 6.49% and Hyperliquid 10.95% — Hyperliquid is the more expensive venue. An agent ranking on the raw decimal picks precisely wrong.
Bake all three fixes into your tool layer, not your prompt. A model will not reliably remember to drop a partial bar, and it should not have to.
What this actually costs in requests
Agents burn quota through loops, not through single calls. Do the arithmetic before you build:
| Agent shape | Calls per cycle | Cadence | Requests/day | Tier |
|---|---|---|---|---|
| Single-coin hourly check | 2 | Hourly | 48 | Free |
| 5 coins, hourly | 10 | Hourly | 240 | Free |
| 5 coins, every 15 min | 10 | 96×/day | 960 | Free (just) |
| 20 coins, every 15 min | 40 | 96×/day | 3,840 | Pro |
| 20 coins, every 5 min + depth | 60 | 288×/day | 17,280 | Pro Plus |
The tiers: free is 10 requests/minute and 1,000/day on a verified key (100/day before you verify), Pro is 30/min and 10,000/day, Pro Plus is 120/min and 50,000/day.
Two habits keep an agent inside its budget:
- Read the headers. Every response carries
x-ratelimit-remaining-minuteandx-ratelimit-remaining-day. Back off from those rather than discovering the ceiling with a 429. - Match cadence to the data. A 1h candle changes once an hour. Open interest and its 30-day trend move slowly. Polling either every 10 seconds spends quota to re-read an identical payload.
If your agent needs broad context rather than one symbol in depth, /api/v1/daily returns a single consolidated market snapshot in one request — it is rebuilt once a day, so treat it as the daily briefing and use the live endpoints above for anything time-sensitive.
Putting it together: one context block
The last mile is turning three JSON payloads into something a model can reason over. Do the interpretation in code and hand the model conclusions, not decimals — LLMs have no intuition for whether 0.00005927 is large:
def market_context(coin="BTC", symbol="BTCUSDT"):
candles = run_tool("get_candles", {"symbol": symbol, "limit": 201})["klines"]
derivs = run_tool("get_derivatives", {"coin": coin})
settled = candles[:-1] # drop the open bar
closes = [c["close"] for c in settled]
chg_24h = (closes[-1] / closes[-25] - 1) * 100
binance = derivs["binance"]
rate_8h = binance["funding"]["current_rate"]
oi_usd = derivs["hyperliquid"]["open_interest_usd"]
return (
f"{coin}: ${closes[-1]:,.0f}, {chg_24h:+.2f}% over 24h.\n"
f"Funding {rate_8h * 1095 * 100:+.2f}% annualized "
f"({'longs' if rate_8h > 0 else 'shorts'} paying).\n"
f"Open interest ${oi_usd / 1e9:.2f}B, "
f"30d trend {binance['open_interest']['trend_30d_pct']:+.2f}%."
)Two calls in, three sentences out:
BTC: $78,487, -0.41% over 24h.
Funding +6.49% annualized (longs paying).
Open interest $2.78B, 30d trend +0.94%.That block costs a few dozen tokens and gives the model direction, cost and crowding in language it can act on. Drop it into the system prompt on every turn and the agent stops guessing.
Where to go deeper on each leg: fetching 1h OHLCV candles for BTCUSDT, the current BTC perpetual funding rate, open interest via API, and order-book depth when you need to size the order the book can actually absorb.



