If you are an agent: this page is your map: every path below is live, keyless, and returns JSON you can verify. If you are building with agents: liquidation intelligence with a public record where every level we publish goes on the record, misses included, each figure with its sample size and a Wilson 95% lower confidence bound, plus a falsification record we publish, including the symbols we switched off.
Descriptive measurements of past performance. Never a forecast, never financial advice.
One call returns BTC liquidation walls, snapshot age, evidence labels and the public record. Free data is 2 hours behind on six coins. No key or card.
curl --fail-with-body --max-time 20 https://hunterkiller.io/api/v1/levels/BTCUSDT
Press the button to make one API call. There are no background calls.
The response will appear here.
Keep the delay, age and evidence labels with any result you show. Descriptive measurements, not trading advice. Agent examples and tools. Public record.
One call per coin returns the liquidation walls a bot needs: the nearest wall above and below the mark, every wall with its dollar size, signed distance, age and evidence label, the snapshot time, and a link to the public record. The numbers are the ones the dashboard shows at the same snapshot.
GET /api/v1/levels/{symbol}Free key: a 2 hour delayed snapshot on the Free coins, 60 calls per minute, metered per key. Sign in, then call POST /api/keys/free.
$49 a month or $470 a year. Founding price locked for the first 50 subscribers.
The current snapshot on every covered coin, 120 calls a minute, 100,000 calls a month and the public record link.
Start monthly, $49 Start yearly, $470
Sign in first, then mint your key with POST /api/keys/mine. A free key stays free.
Authorization: Bearer hk_live_...Real time: a Bot API key, an API plan key, a Pro session, prepaid credits on any key (from $10 for 600 calls), or x402 at $0.005 per call in USDC on Base.
Python
import os, requests # pip install requests
KEY = os.environ["HK_API_KEY"] # free key: sign in, then POST /api/keys/free
res = requests.get("https://hunterkiller.io/api/v1/levels/BTCUSDT",
headers={"Authorization": "Bearer " + KEY}, timeout=10)
res.raise_for_status() # 401 no key, 403 coin needs paid access, 429 slow down
lv = res.json()
print(lv["symbol"], lv["mark"]["price"], lv["tier"], lv["as_of_iso"], "age", lv["age_sec"], "s")
for name in ("above", "below"):
w = lv["nearest"][name]
if w:
print(name, w["price"], w["distance_label"], f"${w['usd']:,.0f}", w["evidence"])Node
// Node 18+ (built-in fetch)
const key = process.env.HK_API_KEY;
const res = await fetch("https://hunterkiller.io/api/v1/levels/BTCUSDT", {
headers: { Authorization: `Bearer ${key}` },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const lv = await res.json();
console.log(lv.symbol, lv.mark.price, lv.tier, lv.as_of_iso, "age", lv.age_sec, "s");
for (const [name, w] of Object.entries({ above: lv.nearest.above, below: lv.nearest.below })) {
if (w) console.log(name, w.price, w.distance_label, Math.round(w.usd), w.evidence);
}curl
# one coin curl -s -H "Authorization: Bearer $HK_API_KEY" https://hunterkiller.io/api/v1/levels/BTCUSDT # up to 10 coins in one request, metered one call per coin curl -s -H "Authorization: Bearer $HK_API_KEY" "https://hunterkiller.io/api/v1/levels?symbols=BTCUSDT,ETHUSDT,SOLUSDT" # your metered calls today curl -s -H "Authorization: Bearer $HK_API_KEY" https://hunterkiller.io/api/v1/levels/usage # keyless: the x402 price quote (402), then pay per call in USDC on Base curl -s "https://hunterkiller.io/api/v1/levels/BTCUSDT?live=1"
Freqtrade: skip an entry when a big wall sits between price and your target. The same check fits a Hummingbot script or a 3Commas webhook relay.
# In your strategy under user_data/strategies: add confirm_trade_entry to your IStrategy
import os, time, requests
from freqtrade.strategy import IStrategy
HK = "https://hunterkiller.io/api/v1/levels/"
HEAD = {"Authorization": "Bearer " + os.environ["HK_API_KEY"]}
MAX_WALL_PCT, MIN_WALL_USD = 1.5, 50e6 # your rule: tune both
_cache = {}
def hk_levels(pair):
sym = pair.split("/")[0] + "USDT" # "BTC/USDT:USDT" becomes "BTCUSDT"
hit = _cache.get(sym)
if hit and time.time() - hit[0] < 60: # one call per coin per minute is plenty
return hit[1]
lv = requests.get(HK + sym, headers=HEAD, timeout=5).json()
_cache[sym] = (time.time(), lv)
return lv
class WallAwareStrategy(IStrategy):
# ... your populate_indicators / populate_entry_trend / populate_exit_trend ...
def confirm_trade_entry(self, pair, order_type, amount, rate, time_in_force,
current_time, entry_tag, side, **kwargs) -> bool:
try:
lv = hk_levels(pair)
except Exception:
return True # data unavailable: never block on us
if not lv.get("ok"):
return True
wall = lv["nearest"]["above" if side == "long" else "below"]
if wall and abs(wall["distance_pct"]) < MAX_WALL_PCT and wall["usd"] > MIN_WALL_USD:
return False # a big wall sits between price and the target
return TrueProp desk data sheet. Fields, cadence, history depth and the public record on one page. OpenAPI 3.0 · /api/v1/levels/symbols
GET /api/v1/levels/usage shows your key's metered calls today.
Modeled levels. Descriptive, not a forecast and not a trade instruction. Nothing here places an order.
Start here: each file is generated from the same endpoint registry, so they can never disagree:
The capability manifest: every public endpoint with method, auth tier, rate limit, params and response fields, plus the MCP block and the auth/pricing surfaces.
The concise index and the extended version: per-endpoint params, response fields, curl examples, honesty caveats.
Streamable-HTTP MCP discovery document: server info, protocol versions, and the tool list with per-tool access tiers.
OpenAPI 3.0 spec for the free endpoints, generated from the same registry with live rate limits in every operation.
Claude Code:
claude mcp add --transport http hunterkiller https://hunterkiller.io/mcp
Claude Desktop or Cursor (claude_desktop_config.json or .cursor/mcp.json):
{
"mcpServers": {
"hunterkiller": { "url": "https://hunterkiller.io/mcp" }
}
}OpenAI function calling. The same schema is served at GET /agents/tools.openai.json:
{
"tools": [
{
"type": "function",
"function": {
"name": "desk_read",
"description": "Call before placing a leveraged perp order. Returns where liquidation money is stacked, where your position would be taken out, how far a cascade runs, who is paying funding, and a descriptive verdict (wall_in_band, no_wall_in_band or thin_data) with stop and target suggestions. Free delayed data without a key. Descriptive, not a forecast.",
"parameters": {
"type": "object",
"properties": {
"symbol": {
"type": "string",
"description": "Perp pair, e.g. BTCUSDT. See GET /api/desk_read/symbols."
},
"side": {
"type": "string",
"enum": [
"long",
"short"
],
"description": "Your position side."
},
"entry": {
"type": "number",
"description": "Your entry price."
},
"leverage": {
"type": "number",
"description": "Your leverage, e.g. 20."
},
"ref": {
"type": "string",
"description": "Your agent name, for attribution."
}
},
"required": [
"symbol"
],
"additionalProperties": false
}
}
}
]
}Python: call the Desk Read before an order and skip when liquidation is too close.
import json, os, urllib.error, urllib.request
MIN_DISTANCE_PCT = 6.0 # skip when liquidation is closer than this
def desk_read(sym, side, entry, lev):
url = (f"https://hunterkiller.io/api/desk_read?symbol={sym}&side={side}"
f"&entry={entry}&leverage={lev}&ref=my-agent")
req = urllib.request.Request(url)
if os.environ.get("HK_API_KEY"):
req.add_header("Authorization", "Bearer " + os.environ["HK_API_KEY"])
return json.load(urllib.request.urlopen(req, timeout=10))
def place_order(sym, side, entry, lev):
... # your exchange call
for sym, side, entry, lev in [("BTCUSDT", "long", 81000, 20)]:
try:
read = desk_read(sym, side, entry, lev)
except urllib.error.HTTPError as e: # 404 unknown_symbol, 422 invalid_number
print("no trade, desk read refused:", e.read().decode()); continue
hurt = read["hurt"]
if hurt is None or hurt["distance_pct"] < MIN_DISTANCE_PCT:
print("skip", sym, read["hurt_reason"] or read["text"]); continue
print(read["verdict"], read["text"])
place_order(sym, side, entry, lev)TypeScript: the same loop.
const MIN_DISTANCE_PCT = 6; // skip when liquidation is closer than this
const key = process.env.HK_API_KEY;
async function deskRead(sym: string, side: string, entry: number, lev: number) {
const url = `https://hunterkiller.io/api/desk_read?symbol=${sym}&side=${side}` +
`&entry=${entry}&leverage=${lev}&ref=my-agent`;
const res = await fetch(url, { headers: key ? { Authorization: `Bearer ${key}` } : {} });
if (!res.ok) throw new Error(await res.text()); // 404 unknown_symbol, 422 invalid_number
return res.json();
}
async function placeOrder(sym: string, side: string, entry: number, lev: number) {
// your exchange call
}
for (const [sym, side, entry, lev] of [["BTCUSDT", "long", 81000, 20]] as const) {
const read = await deskRead(sym, side, entry, lev).catch((e) => (console.log("no trade:", String(e)), null));
if (!read) continue;
if (!read.hurt || read.hurt.distance_pct < MIN_DISTANCE_PCT) {
console.log("skip", sym, read.hurt_reason ?? read.text); continue;
}
console.log(read.verdict, read.text);
await placeOrder(sym, side, entry, lev);
}Python SDK: pip install hunterkiller, then desk_read(), watch() (a stream that tells you when a wall comes into range) and symbols(). Source in the public SDK repo.
TypeScript SDK: npm install hunterkiller-sdk, then deskRead(), watch() and symbols(). Same repo.
Specs: OpenAPI 3.0 · Postman collection · covered symbols: /api/desk_read/symbols.
One call, five plain-word answers for a perp pair, plus
lines[] and a ready-to-send text block. REST: GET /api/desk_read?symbol=BTCUSDT
(optional side, entry, leverage, ref). MCP tool:
desk_read on /mcp. An agent that pays per call is a subscriber.
pull.source is band for the configured band selector,
near for the near pair when the band is empty, or ladder
for the nearest usable ladder walls when both pairs are empty. At the same rounded payload price,
a source change resets the watch baseline without a pull_flip or
wall_in_range event.Pay with a card. Prepaid packs from $10, bought with a card, 600 live calls.
Any API key holder, a free key included, can buy one: POST /api/agents/credits/checkout
with {"pack_id": "p10"} (also p50, p200) returns a Stripe Checkout URL, or call the MCP tool
buy_credits and hand the URL to your human. GET /api/agents/credits shows the balance. With credits a keyed
Desk Read is live and spends one credit per request (one per watch event); X-HK-Credits-Remaining shows what is left,
and at zero the call falls back to the key's normal tier. Packs: $10 for 600 calls, $50 for 3,500, $200 for 16,000.
x402 stays available next to it; the x402 Bazaar listing appears after the first settled payment.
Free vs paid. A keyless call is free and gets the delayed tier.
A B2B API key (Authorization: Bearer <key> or x-api-key), a Pro session, or an x402 payment gets live data.
Add live=1 with no payment to receive the 402 price quote (default $0.02 USDC on Base per call; the live price is
in the 402 response and in access.price_usd_per_live_call). The x402 client flow is the one in
examples/first-purchase. Rate limits. Keyless REST shares the global per-IP limiter quoted at
/developers; keyless MCP is capped at 10 tool calls per minute per IP; keyed calls follow your key's plan limits.
Over the limit returns 429 with Retry-After. Add ref=your-agent-name so we can see which agents use it.
Descriptive, not a forecast.
curl -s "https://hunterkiller.io/api/desk_read?symbol=BTCUSDT&side=long&entry=81000&leverage=20&ref=my-agent"
import json, os, urllib.request
url = "https://hunterkiller.io/api/desk_read?symbol=BTCUSDT&side=long&entry=81000&leverage=20&ref=my-agent"
req = urllib.request.Request(url)
if os.environ.get("HK_API_KEY"):
req.add_header("Authorization", "Bearer " + os.environ["HK_API_KEY"])
read = json.load(urllib.request.urlopen(req))
print(read["text"])const key = process.env.HK_API_KEY;
const res = await fetch(
"https://hunterkiller.io/api/desk_read?symbol=BTCUSDT&side=long&entry=81000&leverage=20&ref=my-agent",
{ headers: key ? { Authorization: `Bearer ${key}` } : {} },
);
const read = await res.json();
console.log(read.text);wall_in_band, no_wall_in_band or thin_data. It reads the same wall set as
flags: wall_in_band when the nearest wall on either side is under 1% from price (the same number as
flags.liq_distance_pct, so it always agrees with flags.wall_within_1pct), no_wall_in_band when walls exist but none
is that close, thin_data when there is no price or no wall data. It is never null and never a recommendation.
proceed, reduce and skip are no longer emitted (changed with the 0.4.1 docs). verdict_reasons[] names the wall,
its distance and its source; a ladder fallback wall is labeled as such.verdict_long and verdict_short are each
{verdict, confidence, confidence_basis, reasons[]} and apply the same test to that side's losing-side wall (below for a long,
above for a short), with the liquidation distance of a hypothetical entry at the current price and
assumed_leverage (default 10) in the reasons. verdict_side names the side the pull favours, then long, and
bias is up, down or flat from the pull. One call per symbol is enough.verdict_side echoes your side, its entry uses your liquidation distance, and verdict_other is the
opposite side as a hypothetical. Send a side alone, without entry and leverage, and it is evaluated the same hypothetical way.null, with confidence_basis "unscored". No Desk Read verdict is scored yet, so no number is
shown. When the public ledger scores verdicts, confidence returns with confidence_basis "scored" and a documented floor.
thin_data is now a verdict value, not a confidence floor.null only when the price feed is down.plan.stop and plan.first_target.When hurt or plan is null, hurt_reason or
plan_reason says why. Three values:
no_position_given: you did not send a valid side, entry and leverage.price_past_liquidation: price is already through the liquidation level for that position, so there is no way to describe.no_wall_in_range: your position is valid but no sized wall sits on the losing side strictly between price and your liquidation (the same wall hurt names), or there is no wall on the other side, so there is no stop or target to suggest. stop_suggestion and target_suggestion are then null too.Bad input fails loudly. An unknown symbol returns 404
{"error":"unknown_symbol","symbol":"FOO","did_you_mean":[...],"symbols_url":"/api/desk_read/symbols"}. A non-numeric entry or leverage
returns 422 {"error":"invalid_number","field":"entry"}. The MCP tool returns the same payload with isError: true.
A bare base like BTC is accepted as BTCUSDT only because that pair is covered; FOO is never rewritten to FOOUSDT.
List what is covered with GET /api/desk_read/symbols (free and live tiers, the free-tier delay, cached 5 minutes).
Responses carry RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset from the limiter that served you,
Cache-Control: public, max-age=20 on the free tier and private, no-store on live, and X-HK-Data-Age-Seconds.
The 402 body adds message and docs; the x402 headers are unchanged.
No key, no signup. The delayed cross-exchange liquidation tape and the full measured track record, as public JSON. Full docs with params and response fields: /developers.
GET /api/public/liq_teaserCross-exchange liquidation tape, majors only, delayed on the free tier. 24h totals + last 40 events ≥ $5k.
GET /api/public/coin_teaser/{symbol}Per-coin liquidation snapshot for any tracked perp symbol (BTCUSDT, ETHUSDT, …). Unknown symbols 404 honestly.
GET /proof/statsThe public record: every level we publish goes on the record, misses included. Released counts and dates, each with n and a Wilson 95% lower bound.
GET /proof/evidence · /proof/ledger.csvThe complete published ledger, including auto-disabled symbols. Publishing what we switched off is the point.
GET /proof/anchorsDaily cryptographic hash anchors over the proof artifacts. Verify the ledger's integrity independently.
GET /proof/feed_trust · /feeds/trustPer-venue reconciliation verdicts on our own tape, against independent exchange references, with n shown.
curl -s https://hunterkiller.io/api/public/liq_teaser curl -s https://hunterkiller.io/proof/stats
Find the managed OAuth server in the official MCP Registry or on MCP.so.
One streamable-HTTP endpoint at /mcp, stateless: a single JSON-RPC 2.0 request per POST, JSON response.
Keyless clients get protocol discovery plus three free tools (get_proof_summary, get_feed_trust_summary,
get_magnet_levels_teaser) built only from surfaces the public website already publishes. The graded data tools
(calibrated magnets, reach stats, …) require a B2B API key, metered identically to the REST /api/v1 API.
# 1. initialize (once per client)
curl -s -X POST https://hunterkiller.io/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"}}}'
# 2. list the tools
curl -s -X POST https://hunterkiller.io/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# 3. call a free tool (no key)
curl -s -X POST https://hunterkiller.io/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_proof_summary","arguments":{}}}'Discovery first: GET /.well-known/mcp.json: protocol versions, server info, and the tool list with per-tool tiers.
/api/public/coin_teaser/*, /coins/*).429 with a Retry-After header; responses carry
Cache-Control: public; cache them.The paid B2B Data API (/api/v1/*) requires an API key
(Authorization: Bearer <key> or x-api-key), is metered, and serves real-time data across the full
tracked universe: the same validated data layer the MCP data tools use. Sign up at /signup;
key self-management lives at /api/keys/mine (login required). Docs: /docs. Pricing:
/pricing.
An autonomous agent cannot do an email
signup or a checkout page, so it does not have to. Two machine rails, same USDC on Base, both honest
about their own state (an unconfigured rail says dormant and advertises nothing):
/api/v1/magnets keyless,
get 402 with the payment requirements (both wire shapes: PAYMENT-REQUIRED
header + v1 body), sign the USDC authorization, retry with X-PAYMENT /
PAYMENT-SIGNATURE; the facilitator settles on-chain and the answer carries
X-PAYMENT-RESPONSE with the settlement receipt. Replay-hardened: every authorization
nonce is single-claimed before settlement./api/payg/agent_bootstrap, receive an API key with the paid balance
($0.01/call, 402 at zero; top up via /api/payg/agent_topup).# terms are served live (dormant until the receiving address is published)
curl -s https://hunterkiller.io/agents.json | jq '.pricing.machine, .pricing.machine.x402'
# balance rail, after paying USDC on Base to pricing.machine.pay_to:
curl -X POST https://hunterkiller.io/api/payg/agent_bootstrap \
-H 'content-type: application/json' \
-d '{"tx_hash": "0x..."}'
# -> {"api_key": "hk_...", "credited_usd": ...} (the key is shown ONCE)
# x402 per-call rail: any x402 client library handles the 402 -> sign -> retry flow
# (pip install x402 / @x402/fetch) against https://hunterkiller.io/api/v1/magnets
# Coinbase Payments MCP (agents.coinbase.com/mcp, npx @coinbase/payments-mcp):
# its wallet speaks x402 on Base USDC, so these endpoints are payable by it out
# of the box; the agent sees the 402 requirements and pays, no API key, no signupCompatible wallets: any x402-capable
agent wallet, including Coinbase's Payments MCP (agents.coinbase.com/mcp,
npx @coinbase/payments-mcp). The per-call requirements above are standard x402 on
Base USDC, so a Payments-MCP agent pays without keys or account setup. On-chain verification:
contract, 2+ confirmations, amount floor, replay-guarded. A payment that does not verify is never credited.
Rate limits: institutional keys 120/min, pay-as-you-go 30/min, x402 per-call settles at chain speed.