Buildix is an institutional-grade orderflow analytics platform for DEX and CEX perpetual trading. Access real-time CVD, VPIN, OBI, whale tracking and signal data over a REST API from Python, JavaScript or cURL. All endpoints are documented below. There is no WebSocket: the platform is serverless and holds no connections, so consumers poll, and the feed endpoints report their producer cadence so you know how often that is worth doing.
Add Buildix to Claude, ChatGPT, Le Chat, Perplexity, Grok or any MCP client. Free mode: one URL,six read-only tools, no account and no key. API mode: the same API keys and daily quota as the REST API, fifteen plan-gated tools with raw values.
https://www.buildix.trade/api/mcp/publicBoth modes and the setup for each assistant: AI Integrations (API mode).
Funding annualization, October 2, 2026. fundingAnnualized (screener, pair, signals, CSV export) and funding_annualized_pct (alert webhooks) are now annualized on the venue's real settlement interval. Hyperliquid and dYdX settle hourly, so on Hyperliquid the values are about 8 times the previous ones for the same rate. funding, the raw hourly rate, does not change. In /api/v1/funding-arb every venue carries intervalHours, annualized uses it, and spread and spreadAnnualized compare hourly equivalent rates.
Counted per API key, reset at Europe/Rome midnight (not UTC). Exhausting it returns HTTP 429 with a Retry-After header holding the seconds until the reset. Whale moved from 50,000 to 2,000 requests per day in September 2026 and the 50,000 allowance moved to the API plan; a Whale subscription that started before 8 September 2026 keeps 50,000 per day for 60 days from that date. Read /api/v1/usage for the limit actually applied to your key.
The burst guard runs at the edge and does not depend on your plan. It is keyed by identity: your IP address, or your IP plus the public prefix of the key when you send one, so a shared office address is not punished for one noisy key. Going over returns HTTP 429 with the window in the body and an accurate Retry-After.
/api/v1/traders, /api/v1/traders/{address}/score and /api/v1/consensus (and the MCP tools get_top_traders, get_trader_score and get_smart_money_consensus) rank the wallets the whale tracker follows. Read these limits before using a number:
Buildix does not execute trades. Data only, not investment advice.
Since 2026-11-07 the order is the official Top Traders rank (Follow Score). How wallets enter and how the score is built: methodology.
/api/leaderboard is deprecated since 2026-11-07 and stops answering on 2027-01-07. Use /api/v1/traders (official Top Traders rank) instead.
/api/v1/universe, /api/v1/trade-plan, /api/v1/exit-radar, /api/v1/copy-portfolio, /api/v1/fade-list, /api/v1/dumb-money, the Copy Signal filters of /api/v1/whale-moves and the whale.copy_signal webhook share these limits. Read them before using a number:
Same payload, limits and plan as the REST twin, same API key and daily quota. API keys are issued on Whale and API, so a key reaches the Pro tools too.
| tool | plan | REST twin | returns |
|---|---|---|---|
| get_trade_plan | PRO | /api/v1/trade-plan | Reference levels of the open positions of one wallet from its own recorded episodes. |
| get_exit_radar | PRO | /api/v1/exit-radar | High-score wallets leaving positions in the last 24 hours, clusters and per-coin totals. |
| get_copy_portfolio | PRO | /api/v1/copy-portfolio | The copy portfolios of the account that owns the key: live record, backtest, overlap. |
| get_fade_list | WHALE | /api/v1/fade-list | Wallets with a significantly negative edge at the detection tick, and the simulated opposite side. |
| get_dumb_money_index | WHALE | /api/v1/dumb-money | Low-score wallets next to high-score wallets, per coin. |
The Copy Signal filters have no MCP tool: an agent reads new moves through the webhook or the pull feed (see Copy Signal webhooks below).
Every tradable Hyperliquid perpetual with mark price, 24h change, 24h volume, open interest in USD and funding (raw hourly rate + annualized as rate x 24 x 365). No API key required; a key only raises the daily quota.
curl "https://www.buildix.trade/api/v1/screener" # A key is optional here, and raises the daily quota: curl "https://www.buildix.trade/api/v1/screener" \ -H "Authorization: Bearer YOUR_API_KEY"
Generate an API key from your Dashboard → API Keys. Include it in every request as a Bearer token. Keys look like bx_ followed by 32 hex characters; there is no bx_live_ / bx_test_ split.
Authorization: Bearer bx_0123456789abcdef0123456789abcdef
Two other spellings are accepted, in this order of priority: the header x-api-key: bx_..., and, only where a client cannot set a custom header at all, the query parameter ?api_key=bx_.... A key in a URL can be captured by your own client logs and by any proxy in between, so keep the query form for connectors that leave you no choice and give those a dedicated key.
/api/v1/screener and /api/v1/pair/{symbol} answer without a key; a key on those only raises the daily quota. Every other endpoint requires one. An invalid or revoked key returns 401, a plan below the endpoint returns 403 naming the tier required, and an exhausted daily quota returns 429.
Buildix can POST events to your own server: Entry Signals, their outcome four hours later, your alerts and, on Whale and API, Copy Signals. Register an endpoint in Dashboard → Alerts (5 endpoints on Pro, 20 on Whale and API). Each endpoint gets a signing secret, shown once, that you can rotate.
entry_signal.created: a new Entry Signal matching an Entry Signal alert that uses the endpoint (same pairs, timeframe and minimum grade as the Telegram and email channel). Pro and above. On HIP-3 markets (symbols such as xyz:NVDA) a signal whose bar falls outside the market's trading session is sent with its grade lowered one step and two extra fields in data: session ("closed") and grade_before_session. Crypto signals and open-session signals never carry them.signal.outcome: four hours after each delivered signal, the move in the signal's direction, the best and worst excursion and which level was touched first. Computed from the 5m bars Buildix already holds, with the same price rule as the public channel follow up.alert.triggered: an alert (price, screener, sweep, delta flip, drawing) whose webhook URL is a registered endpoint. data carries the same fields as the previous flat payload, plus alert_kind.whale.copy_signal: a move of a tracked wallet that passes one of your Copy Signal filters. Whale and API. Fixed text, limits, filters, payload and templates in Copy Signal webhooks.test: sent by the Send test event button.Every event has the same envelope. id is stable: a retry of the same event carries the same id, so store it and ignore duplicates. New fields may be added to data; a breaking change would ship as version "2".
{
"id": "evt_9f2c41d0b6a84e5f8c1d2e3f4a5b6c7d",
"type": "entry_signal.created",
"version": "1",
"created_at": "2026-09-26T10:15:04.211Z",
"data": {
"signal_id": "BTC-15m-1758880800000-long",
"symbol": "BTC",
"timeframe": "15m",
"side": "long",
"grade": "A",
"score": 0.81,
"bar_time": "2026-09-26T10:00:00.000Z",
"regime": "trend_up",
"confirmed": true,
"flow_source": "trades",
"logics": { "liquidity_grab": true, "flow_rejection": true, "absorption": false,
"cvd_divergence": true, "structure_shift": false, "level_confluence": true },
"plan": { "entry": 64210.5, "stop": 63880.0, "t1": 64706.0, "t2": 65201.5, "r_t1": 1.5, "r_t2": 3 },
"price_at_signal": 64215.0,
"chart_url": "https://www.buildix.trade/pair/BTC"
}
}{
"id": "evt_0b7e...",
"type": "signal.outcome",
"version": "1",
"created_at": "2026-09-26T14:15:30.002Z",
"data": {
"entry_event_id": "evt_9f2c41d0b6a84e5f8c1d2e3f4a5b6c7d",
"signal_id": "BTC-15m-1758880800000-long",
"symbol": "BTC", "timeframe": "15m", "side": "long", "grade": "A",
"horizon": "4h",
"signaled_at": "2026-09-26T10:15:04.211Z",
"evaluated_at": "2026-09-26T14:15:30.002Z",
"price_at_signal": 64215.0,
"price_after": 64890.0,
"move_pct": 1.0512,
"max_favorable_pct": 1.4201,
"max_adverse_pct": -0.3105,
"first_level_hit": "t1",
"t2_hit": false
}
}first_level_hit is stop, t1, none, or both_same_bar when one 5m bar spans both levels and the order inside the bar is unknown.
Content-Type: application/json X-Buildix-Event: entry_signal.created X-Buildix-Event-Id: evt_9f2c41d0b6a84e5f8c1d2e3f4a5b6c7d X-Buildix-Delivery: <uuid of this delivery> X-Buildix-Attempt: 1 X-Buildix-Timestamp: 1758881704 X-Buildix-Signature: v1=<hex HMAC-SHA256(secret, "<timestamp>.<raw body>")>
Compute the HMAC over the timestamp, a dot and the raw request body exactly as received, and compare in constant time. Reject a timestamp more than 300 seconds away from your clock (replay protection) and an event id you already handled. For 24 hours after a rotation the header carries two entries, v1=<new>,v1=<old>: accept the request when any entry matches.
// Node 18+ (Express). Verify on the RAW body, before JSON parsing.
import crypto from 'node:crypto';
import express from 'express';
const SECRET = process.env.BUILDIX_WEBHOOK_SECRET; // whsec_...
const TOLERANCE_S = 300;
const seen = new Set(); // use your database in production
function verify(rawBody, timestamp, signatureHeader) {
if (!timestamp || !signatureHeader) return false;
const ts = Number(timestamp);
if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > TOLERANCE_S) return false;
const expected = crypto.createHmac('sha256', SECRET).update(`${ts}.${rawBody}`).digest();
return signatureHeader.split(',').some((part) => {
const [scheme, hex] = part.trim().split('=');
if (scheme !== 'v1' || !/^[0-9a-f]{64}$/.test(hex || '')) return false;
return crypto.timingSafeEqual(Buffer.from(hex, 'hex'), expected);
});
}
const app = express();
app.post('/buildix', express.raw({ type: 'application/json' }), (req, res) => {
const raw = req.body.toString('utf8');
if (!verify(raw, req.get('X-Buildix-Timestamp'), req.get('X-Buildix-Signature'))) {
return res.status(401).end();
}
const event = JSON.parse(raw);
if (seen.has(event.id)) return res.status(200).end(); // retry of an event already handled
seen.add(event.id);
res.status(200).end(); // answer fast, work after
// handle event.type / event.data here
});
app.listen(3000);# Python 3.10+ (Flask). Verify on the RAW body, before JSON parsing.
import hashlib, hmac, json, os, time
from flask import Flask, request, abort
SECRET = os.environ["BUILDIX_WEBHOOK_SECRET"].encode() # whsec_...
TOLERANCE_S = 300
seen = set() # use your database in production
def verify(raw: bytes, timestamp: str | None, signature_header: str | None) -> bool:
if not timestamp or not signature_header or not timestamp.isdigit():
return False
if abs(time.time() - int(timestamp)) > TOLERANCE_S:
return False
expected = hmac.new(SECRET, f"{timestamp}.".encode() + raw, hashlib.sha256).hexdigest()
for part in signature_header.split(","):
scheme, _, value = part.strip().partition("=")
if scheme == "v1" and hmac.compare_digest(value, expected):
return True
return False
app = Flask(__name__)
@app.post("/buildix")
def buildix():
raw = request.get_data()
if not verify(raw, request.headers.get("X-Buildix-Timestamp"), request.headers.get("X-Buildix-Signature")):
abort(401)
event = json.loads(raw)
if event["id"] in seen:
return "", 200 # retry of an event already handled
seen.add(event["id"])
# handle event["type"] / event["data"] here
return "", 200Buildix sends data. Execution, sizing and risk are yours.
detection.delay_s is the mean detection delay of that wallet (delay_basis: "poll_meta"), or 600 with note: "assumed 10 min" when Buildix has no poll data for it. detected_at is when Buildix saw the change.entry_px is the wallet's average entry, mark_px the coin's mark at detection; notional fields are size x entry price.conviction and trade_plan are null when there is not enough data; the trade plan declares its own coverage.detected_at and drop signals that are too old for your strategy.whale.copy_signal.| Minimum score | Trader Score 0 to 100 at or above |
| Minimum confidence | low, medium or high |
| Minimum conviction | size of the move against the wallet's usual size: 1.5x+ or 3x+ (moves without conviction data do not pass) |
| Coins / exclude coins | for example BTC, ETH, xyz:NVDA |
| Copyable at size | only coins whose visible book absorbs a $10k, $100k or $1M market order within 15 bps |
| Direction | LONG only or SHORT only |
| Skip entries against funding | drops opens and adds on the side paying 0.01% per hour or more |
| Fade List as opposite signal | moves of Fade List wallets arrive with sides inverted and signal "fade"; the score, confidence and conviction minimums do not apply to them |
| Actions | open, add, trim, close (a flip is a close plus an open) |
POST <your URL> Content-Type: application/json X-Buildix-Event: whale.copy_signal X-Buildix-Event-Id: evt_... (same on every retry: deduplicate on it) X-Buildix-Delivery: <uuid of this delivery> X-Buildix-Attempt: 1 X-Buildix-Timestamp: <unix seconds of this attempt> X-Buildix-Signature: v1=<hex HMAC-SHA256(secret, timestamp + "." + raw body)>
Reject a timestamp more than 300 seconds from your clock (replay protection). For 24 hours after a secret rotation the header carries two v1= entries: accept the request when any matches. Answer 2xx fast and do the slow work after answering.
{
"id": "evt_cdbf49868beedb8c0e1eb5cb3fa45df5",
"type": "whale.copy_signal",
"version": "1",
"created_at": "2026-10-30T14:14:46.020Z",
"data": {
"signal": "copy",
"notice": "Buildix sends data. Execution, sizing and risk are yours.",
"move": {
"address": "0xf08193e61aedd3139bcb0c95c773e3c65de9e39e", "label": null, "coin": "ZEC",
"action": "open", "action_raw": "opened_long",
"side_before": null, "side_after": "long", "side_basis": "action",
"size_before": 0, "size_after": 3312.06,
"notional_before_usd": 0, "notional_after_usd": 4077537.345492,
"entry_px": 1231.1182, "mark_px": 1224.3, "leverage": 10,
"detected_at": "2026-10-30T14:14:44.128Z"
},
"legs": [{ "action": "open", "side": "long" }],
"trader": {
"score": 51.2, "confidence": "low", "provisional": true, "closed_episodes": 1,
"style": { "kind": "swing", "badge": "Swing trader, HYPE/ZEC, 7x median, long-biased" },
"coverage": { "covered_since": "2026-10-17T00:00:00Z", "n_episodes": 1, "n_days": 14, "coverage_complete": false },
"scores_computed_at": "2026-10-30T18:14:48.515Z"
},
"conviction": null,
"trade_plan": null,
"capacity": {
"coin": "ZEC", "visible_depth": true, "book_at": "2026-10-30T17:54:34Z",
"k10": { "slippage_bps": 2.2, "copyable": true },
"k100": { "slippage_bps": 3.4, "copyable": true },
"m1": { "slippage_bps": 10.2, "copyable": true }
},
"funding_1h": 0.0000125,
"detection": { "delay_s": 300, "delay_basis": "poll_meta", "note": null,
"poll_tier": "A", "poll_interval_s": 600, "detect_gap_s": null,
"detected_at": "2026-10-30T14:14:44.128Z" },
"matched_filters": ["cf_3f9a2c1d7b6e4a50"]
}
}import hashlib, hmac, time
def verify_buildix(secret: str, raw_body: bytes, ts: str, sig_header: str, tolerance_s: int = 300) -> bool:
if not ts or not ts.isdigit() or abs(time.time() - int(ts)) > tolerance_s:
return False
expected = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
for part in (sig_header or "").split(","):
scheme, _, value = part.strip().partition("=")
if scheme == "v1" and hmac.compare_digest(value, expected):
return True
return Falseimport crypto from 'node:crypto';
export function verifyBuildix(secret, rawBody /* Buffer */, ts, sigHeader, toleranceS = 300) {
if (!/^\d+$/.test(ts || '') || Math.abs(Date.now() / 1000 - Number(ts)) > toleranceS) return false;
const expected = crypto.createHmac('sha256', secret).update(`${ts}.`).update(rawBody).digest('hex');
return (sigHeader || '').split(',').some((part) => {
const [scheme, value] = part.trim().split('=');
return scheme === 'v1' && value && value.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(value), Buffer.from(expected));
});
}Receives the webhook, verifies it, answers at once and hands each signal to your own trading code. Sizing, risk limits and the exchange key stay in your code.
# pip install flask
import json, os, queue, threading, time
from datetime import datetime
from flask import Flask, request, abort
SECRET = os.environ["BUILDIX_WEBHOOK_SECRET"] # whsec_..., shown once in Dashboard > Alerts
MAX_SIGNAL_AGE_S = 15 * 60 # your choice: ignore older signals (retries can arrive late)
jobs: "queue.Queue[dict]" = queue.Queue()
seen: dict[str, float] = {} # event id -> time; use Redis or a DB in production
app = Flask(__name__)
@app.post("/buildix")
def buildix():
raw = request.get_data() # exact bytes, before any JSON parsing
if not verify_buildix(SECRET, raw, request.headers.get("X-Buildix-Timestamp", ""),
request.headers.get("X-Buildix-Signature", "")):
abort(401)
event = json.loads(raw)
if event["id"] in seen or event["type"] != "whale.copy_signal":
return "", 200 # duplicate (retry) or another event type
seen[event["id"]] = time.time()
jobs.put(event["data"]) # answer fast, work after
return "", 200
def worker():
while True:
data = jobs.get()
detected = datetime.fromisoformat(data["move"]["detected_at"].replace("Z", "+00:00")).timestamp()
if time.time() - detected > MAX_SIGNAL_AGE_S:
continue
# Your code from here: your sizing, your risk limits, your exchange key.
# e.g. hybridpro.on_copy_signal(coin=data["move"]["coin"], legs=data["legs"], signal=data["signal"],
# score=(data["trader"] or {}).get("score"), capacity=data["capacity"], funding_1h=data["funding_1h"])
print("copy signal", data["signal"], data["move"]["coin"], data["legs"])
threading.Thread(target=worker, daemon=True).start()
# verify_buildix: see "Verify the signature, Python" above.Buildix MCP (API mode, your Buildix API key) gives the agent the data: get_trader_score, get_trade_plan, get_exit_radar, get_top_traders, get_smart_money_consensus, get_fade_list, get_dumb_money_index. Orders go through a separate exchange tool that you run on your own machine with your own exchange key: Buildix never sees that key and never places an order. MCP does not push events: new moves reach the agent through the webhook above (Template 1) or the pull feed below.
claude mcp add --transport http buildix https://www.buildix.trade/api/mcp --header "Authorization: Bearer bx_..."
Example prompt:
When I paste a Copy Signal, and every 30 minutes for the wallets I follow: call get_trader_score for the wallet and skip it unless confidence is medium or high. Call get_trade_plan for the wallet and coin: skip the entry when the plan is collecting or the mark is already past chase_limit_px. Call get_exit_radar for the coin and skip it when high-score wallets are leaving that side. Otherwise propose an order with my exchange tool: at most 1% of my equity per position, never more than 3 open positions, stop at stop_ref_px. Ask me before sending any order and log every decision with the signal id.
GET /api/v1/whale-moves (Whale, API key) accepts the same filters: coins, exclude_coins, side, actions, min_score, min_confidence, conviction_min, copyable, exclude_against_funding, exclude_fade or fade. With these filters a response scans at most 1,500 rows, so it can hold fewer rows than limit (even none) while has_more is true: keep following next_cursor. See the endpoint tab above for every field.
Need API access?
The screener and pair endpoints are open, so you can build against them before you pick a plan. A key raises the daily quota and unlocks the gated endpoints; the burst limit per minute is the same on every plan.