Gordo Trader API
Programmatic access to your Gordo Trader account. Manage strategies, monitor positions, access market data, and automate your trading workflow.
https://api.gordotrader.com/v1
Quick Start
Generate an API key in your dashboard settings, then make your first request:
curl https://api.gordotrader.com/v1/market/price \
-H "X-API-Key: grd_live_your_key_here"
import requests
API_KEY = "grd_live_your_key_here"
BASE = "https://api.gordotrader.com/v1"
resp = requests.get(f"{BASE}/market/price",
headers={"X-API-Key": API_KEY})
print(resp.json())
# {"coin": "BTC", "price": 84521.30, "change_24h_pct": 2.14, ...}
const API_KEY = "grd_live_your_key_here";
const BASE = "https://api.gordotrader.com/v1";
const resp = await fetch(`${BASE}/market/price`, {
headers: { "X-API-Key": API_KEY },
});
const data = await resp.json();
console.log(data);
// { coin: "BTC", price: 84521.30, change_24h_pct: 2.14, ... }
Authentication
All API requests require an X-API-Key header. Generate keys in your
Settings > API tab.
Permission Scopes
| Scope | Access | Use Case |
|---|---|---|
read |
All GET endpoints | Dashboards, monitoring, analytics |
trade |
Read + close positions, pause/resume strategies, kill switch | Trading bots, risk management |
full |
Trade + update risk limits, manage strategies, run backtests, webhooks | Full automation |
Rate Limits
Rate limits depend on your subscription tier. Limits are applied per API key.
| Tier | Requests / Minute | Requests / Day |
|---|---|---|
| Free | 30 | 1,000 |
| Basic | 60 | 10,000 |
| Pro | 120 | 50,000 |
| Enterprise | 300 | 200,000 |
When you exceed a limit, the API returns 429 Too Many Requests.
Checking Usage
curl https://api.gordotrader.com/v1/account/usage \
-H "X-API-Key: $API_KEY"
resp = requests.get(f"{BASE}/account/usage",
headers={"X-API-Key": API_KEY})
print(resp.json())
# {"total_requests_today": 142, "total_requests_month": 3891, ...}
const resp = await fetch(`${BASE}/account/usage`, {
headers: { "X-API-Key": API_KEY },
});
console.log(await resp.json());
Error Handling
The API uses standard HTTP status codes. Error responses include a JSON body with a detail field.
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad request — check your parameters |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — insufficient scope |
404 | Not found |
429 | Rate limit exceeded |
500 | Internal server error |
Error Response Format
{
"detail": "API key rate limit exceeded: 30 requests/minute for free tier"
}
Handling Errors
import requests
def gordo_get(path, params=None):
resp = requests.get(f"{BASE}{path}",
headers={"X-API-Key": API_KEY},
params=params)
if resp.status_code == 429:
print("Rate limited — back off and retry")
return None
resp.raise_for_status()
return resp.json()
async function gordoGet(path, params = {}) {
const url = new URL(`${BASE}${path}`);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
const resp = await fetch(url, {
headers: { "X-API-Key": API_KEY },
});
if (resp.status === 429) {
console.warn("Rate limited — back off and retry");
return null;
}
if (!resp.ok) throw new Error(`API error ${resp.status}`);
return resp.json();
}
Account
/v1/account/me
Get your account profile, subscription tier, and rate limits.
Response
{
"user_id": 1,
"username": "trader1",
"email": "trader@example.com",
"tier": "pro",
"rate_limits": { "per_minute": 120, "per_day": 50000 },
"key_name": "My Trading Bot",
"key_permissions": "trade"
}
/v1/account/usage
Get aggregate API usage across all your keys.
Response
{
"total_requests_today": 142,
"total_requests_month": 3891,
"keys": [
{
"name": "My Trading Bot",
"key_prefix": "grd_live_a3f2",
"requests_today": 100,
"requests_month": 2500,
"last_used_at": "2026-02-27T07:30:00Z"
}
]
}
Market Data
/v1/market/price
Get the current BTC price and 24-hour change.
resp = requests.get(f"{BASE}/market/price",
headers={"X-API-Key": API_KEY})
price = resp.json()
print(f"BTC: ${price['price']:,.2f} ({price['change_24h_pct']:+.2f}%)")
const { price, change_24h_pct } = await gordoGet("/market/price");
console.log(`BTC: $${price.toLocaleString()} (${change_24h_pct}%)`);
Response
{
"coin": "BTC",
"price": 84521.30,
"change_24h_pct": 2.14,
"high_24h": 85200.00,
"low_24h": 82100.50,
"timestamp": "2026-02-27T08:00:00Z"
}
/v1/market/candles
Get OHLCV candle data. Supports multiple intervals aggregated from 1-minute base data.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
interval | string | 5m | 1m, 5m, 15m, 30m, 1h, 4h, 1d |
limit | int | 500 | Number of candles (max 2000) |
# Get 100 hourly candles
resp = requests.get(f"{BASE}/market/candles",
headers={"X-API-Key": API_KEY},
params={"interval": "1h", "limit": 100})
candles = resp.json()
for c in candles[-3:]:
print(f"{c['timestamp']} O:{c['open']} H:{c['high']} L:{c['low']} C:{c['close']}")
const candles = await gordoGet("/market/candles", {
interval: "1h", limit: "100",
});
candles.slice(-3).forEach(c =>
console.log(`${c.timestamp} O:${c.open} H:${c.high} L:${c.low} C:${c.close}`)
);
Response
[
{
"timestamp": "2026-02-27T07:00:00Z",
"open": 84100.50, "high": 84600.00,
"low": 84050.25, "close": 84521.30,
"volume": 125.43
}
]
/v1/market/funding
Get funding rate history for BTC perpetual futures.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
hours | int | 168 | Lookback in hours (max 8760) |
limit | int | 500 | Max records (max 5000) |
resp = requests.get(f"{BASE}/market/funding",
headers={"X-API-Key": API_KEY},
params={"hours": 72})
for r in resp.json()[-5:]:
print(f"{r['timestamp']} rate={r['funding_rate']:.6f}")
const funding = await gordoGet("/market/funding", { hours: "72" });
funding.slice(-5).forEach(r =>
console.log(`${r.timestamp} rate=${r.funding_rate}`)
);
/v1/market/open-interest
Get open interest snapshots showing aggregate positioning.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
hours | int | 168 | Lookback in hours (max 8760) |
limit | int | 500 | Max records (max 5000) |
coin | string | BTC | Coin symbol |
/v1/market/liquidations
Get detected liquidation burst events — aggregated clusters of liquidation activity.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
hours | int | 168 | Lookback in hours |
limit | int | 100 | Max records (max 500) |
/v1/market/regime
Get the current market regime classification with a composite score.
resp = requests.get(f"{BASE}/market/regime",
headers={"X-API-Key": API_KEY})
regime = resp.json()
print(f"Regime: {regime['regime']} Score: {regime['score']}/100")
const regime = await gordoGet("/market/regime");
console.log(`Regime: ${regime.regime} Score: ${regime.score}/100`);
Response
{
"regime": "trending_up",
"score": 72,
"components": {
"trend_strength": 0.81,
"volatility_percentile": 0.45,
"funding_bias": 0.12,
"oi_momentum": 0.38
},
"timestamp": "2026-02-27T08:00:00Z"
}
Positions
/v1/positions
List all open positions across your strategies.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
mode | string | all | paper, live, canary |
resp = requests.get(f"{BASE}/positions",
headers={"X-API-Key": API_KEY},
params={"mode": "live"})
for pos in resp.json():
pnl = pos.get("unrealized_pnl", 0)
print(f"{pos['strategy_name']} {pos['side']} {pos['size']} @ {pos['entry_price']} PnL: ${pnl:+.2f}")
const positions = await gordoGet("/positions", { mode: "live" });
positions.forEach(pos =>
console.log(`${pos.strategy_name} ${pos.side} ${pos.size} @ ${pos.entry_price}`)
);
/v1/positions/{position_id}
Get full details for a single position, including linked signals and fills.
/v1/positions/{position_id}/close
Requires trade scope
Close a position by emitting a flatten signal. The executor processes it on the next cycle.
resp = requests.post(f"{BASE}/positions/42/close",
headers={"X-API-Key": API_KEY})
print(resp.json())
# {"status": "flatten_signal_emitted", "position_id": 42}
const result = await fetch(`${BASE}/positions/42/close`, {
method: "POST",
headers: { "X-API-Key": API_KEY },
}).then(r => r.json());
// { status: "flatten_signal_emitted", position_id: 42 }
/v1/portfolio
Get a portfolio summary including total equity, unrealized P&L, and exposure breakdown.
Response
{
"total_equity_usd": 52450.00,
"unrealized_pnl": 340.25,
"realized_pnl_today": -120.50,
"open_positions": 3,
"total_exposure_usd": 15200.00,
"available_margin_usd": 37250.00
}
/v1/portfolio/equity
Get equity curve snapshots over time.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
days | int | 30 | Lookback in days (max 365) |
snapshot_type | string | daily | hourly or daily |
Orders & Fills
/v1/orders
List order history.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 100 | Max records (max 500) |
hours | int | 24 | Lookback in hours (max 8760) |
mode | string | all | paper, live, canary |
status | string | all | pending, filled, cancelled |
package_id | int | all | Filter by strategy package |
/v1/orders/{order_id}
Get order details with associated fills.
/v1/fills
List fill history.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 100 | Max records (max 500) |
hours | int | 24 | Lookback in hours (max 8760) |
Signals
/v1/signals/
List signal history — entry, exit, and flatten signals emitted by your strategies.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 100 | Max records (max 500) |
hours | int | 168 | Lookback in hours |
package_id | int | all | Filter by strategy |
action | string | all | long_entry, short_entry, exit, flatten |
/v1/signals/latest
Get the most recent signal from each active strategy. Useful for "last known state" dashboards.
resp = requests.get(f"{BASE}/signals/latest",
headers={"X-API-Key": API_KEY})
for sig in resp.json():
print(f"[{sig['strategy_name']}] {sig['action']} at {sig['price']} conf={sig['confidence']}")
const signals = await gordoGet("/signals/latest");
signals.forEach(s =>
console.log(`[${s.strategy_name}] ${s.action} at ${s.price}`)
);
/v1/signals/{signal_id}
Get full signal detail including metadata and strategy context.
Strategies
/v1/strategies/
List all your strategy packages.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
active_only | bool | false | Only return active strategies |
include_archived | bool | false | Include archived strategies |
resp = requests.get(f"{BASE}/strategies/",
headers={"X-API-Key": API_KEY},
params={"active_only": True})
for s in resp.json():
print(f"{s['display_name']} state={s['state']} mode={s['trading_mode']}")
const strategies = await gordoGet("/strategies/", { active_only: "true" });
strategies.forEach(s =>
console.log(`${s.display_name} state=${s.state} mode=${s.trading_mode}`)
);
/v1/strategies/{package_id}
Get full strategy details including performance stats, current position, and promotion history.
/v1/strategies/{package_id}/pause
/v1/strategies/{package_id}/resume
Requires trade scope
Pause or resume a strategy. Paused strategies stop emitting signals but retain their state.
# Pause strategy #5
requests.post(f"{BASE}/strategies/5/pause",
headers={"X-API-Key": API_KEY})
# Resume later
requests.post(f"{BASE}/strategies/5/resume",
headers={"X-API-Key": API_KEY})
await fetch(`${BASE}/strategies/5/pause`, {
method: "POST", headers: { "X-API-Key": API_KEY },
});
await fetch(`${BASE}/strategies/5/resume`, {
method: "POST", headers: { "X-API-Key": API_KEY },
});
/v1/strategies/{package_id}/risk
Requires full scope
Update risk assumptions for a strategy (max position size, leverage, etc.).
Request Body
{
"risk_assumptions": {
"max_position_usd": 5000,
"max_leverage": 3,
"stop_loss_pct": 2.0,
"take_profit_pct": 5.0
}
}
/v1/strategies/templates
List available strategy templates with their parameter schemas.
/v1/strategies/deployments
Get promotion/rollback history across all strategies.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Max records |
Analytics
/v1/analytics/trades
Get reconstructed round-trip trades with entry/exit prices, P&L, and duration.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Max trades (max 500) |
days | int | 90 | Lookback in days (max 365) |
mode | string | all | paper, live, canary |
resp = requests.get(f"{BASE}/analytics/trades",
headers={"X-API-Key": API_KEY},
params={"days": 30})
trades = resp.json()
wins = [t for t in trades if t["pnl"] and t["pnl"] > 0]
losses = [t for t in trades if t["pnl"] and t["pnl"] < 0]
print(f"Trades: {len(trades)} Win rate: {len(wins)}/{len(wins)+len(losses)}")
print(f"Total PnL: ${sum(t['pnl'] for t in trades if t['pnl']):,.2f}")
const trades = await gordoGet("/analytics/trades", { days: "30" });
const wins = trades.filter(t => t.pnl > 0);
const losses = trades.filter(t => t.pnl < 0);
console.log(`Win rate: ${wins.length}/${wins.length + losses.length}`);
Response
[
{
"strategy": "mean_reversion_v2",
"package_id": 5,
"side": "long",
"entry_price": 83500.00,
"exit_price": 84200.50,
"size": 0.05,
"pnl": 32.53,
"pnl_pct": 0.84,
"fees": 1.72,
"duration_hours": 4.2,
"entry_time": "2026-02-26T14:30:00Z",
"exit_time": "2026-02-26T18:42:00Z",
"status": "closed"
}
]
/v1/analytics/stats
Get aggregate trade statistics for a given period.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
days | int | 90 | Lookback (max 365) |
mode | string | all | paper, live, canary |
Response
{
"period_days": 90,
"total_fills": 342,
"total_volume_usd": 1250000.50,
"total_fees_usd": 312.45,
"avg_slippage_bps": 1.23,
"signals": {"long_entry": 45, "short_entry": 32, "exit": 68, "flatten": 9},
"entries": 77,
"exits": 77
}
/v1/analytics/daily-pnl
Get daily P&L breakdown from equity snapshots.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
days | int | 30 | Lookback (max 365) |
/v1/analytics/notes
/v1/analytics/notes
trade
Trade journal — read and create notes linked to specific trades or strategies.
requests.post(f"{BASE}/analytics/notes",
headers={"X-API-Key": API_KEY},
json={
"content": "Took profit early due to funding rate spike",
"package_id": 5,
"tags": ["funding", "early-exit"]
})
await fetch(`${BASE}/analytics/notes`, {
method: "POST",
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
content: "Took profit early due to funding rate spike",
package_id: 5,
tags: ["funding", "early-exit"],
}),
});
/v1/analytics/export
Export trade data as JSON or CSV.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
days | int | 90 | Lookback |
format | string | json | json or csv |
resp = requests.get(f"{BASE}/analytics/export",
headers={"X-API-Key": API_KEY},
params={"format": "csv", "days": 90})
with open("gordo_trades.csv", "w") as f:
f.write(resp.text)
print("Exported to gordo_trades.csv")
import fs from "fs";
const resp = await fetch(`${BASE}/analytics/export?format=csv&days=90`, {
headers: { "X-API-Key": API_KEY },
});
fs.writeFileSync("gordo_trades.csv", await resp.text());
Risk
/v1/risk/status
Get current risk engine status including kill switch state, leverage, and position limits.
Response
{
"kill_switch_active": false,
"max_leverage": 5,
"max_position_usd": 10000,
"max_daily_loss_usd": 500,
"manual_entry_block": false,
"allocation_guard_active": false,
"auto_throttle_enabled": true,
"position_size_pct": 2.0,
"playbook_budget_pct": 30.0,
"portfolio_budget_pct": 60.0
}
/v1/risk/events
Get risk event log (breaches, throttling, guard activations).
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Max events (max 500) |
hours | int | 168 | Lookback (max 8760) |
severity | string | all | info, warning, critical |
/v1/risk/limits
/v1/risk/limits
full
Get or update your risk limits.
PATCH Request Body
{
"max_leverage": 3,
"max_position_usd": 5000,
"max_daily_loss_usd": 250,
"position_size_pct": 1.5
}
/v1/risk/kill-switch
Requires trade scope
Toggle the emergency kill switch. When active, all trading is halted immediately.
/v1/risk/status to verify the current state.
resp = requests.post(f"{BASE}/risk/kill-switch",
headers={"X-API-Key": API_KEY})
print(resp.json())
# {"kill_switch_active": true, "action": "activated"}
const result = await fetch(`${BASE}/risk/kill-switch`, {
method: "POST",
headers: { "X-API-Key": API_KEY },
}).then(r => r.json());
// { kill_switch_active: true, action: "activated" }
Research
/v1/research/backtest
Requires full scope
Submit a backtest job. Returns a job ID for polling progress and results.
Request Body
{
"template_name": "mean_reversion",
"days": 180,
"parameters": {"lookback_period": 20, "zscore_threshold": 2.0},
"risk_per_trade_pct": 2.0,
"coin": "BTC"
}
import time
# Submit backtest
resp = requests.post(f"{BASE}/research/backtest",
headers={"X-API-Key": API_KEY},
json={"template_name": "mean_reversion", "days": 180,
"parameters": {"lookback_period": 20}})
job_id = resp.json()["job_id"]
# Poll for results
while True:
status = requests.get(f"{BASE}/research/backtest/{job_id}",
headers={"X-API-Key": API_KEY}).json()
print(f"Status: {status['status']} Progress: {status.get('progress', 0)}%")
if status["status"] in ("completed", "failed"):
break
time.sleep(3)
# Get results
results = requests.get(f"{BASE}/research/backtest/{job_id}/results",
headers={"X-API-Key": API_KEY}).json()
print(f"Sharpe: {results['sharpe_ratio']} PnL: {results['total_pnl_pct']}%")
// Submit backtest
const job = await fetch(`${BASE}/research/backtest`, {
method: "POST",
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
template_name: "mean_reversion", days: 180,
parameters: { lookback_period: 20 },
}),
}).then(r => r.json());
// Poll for results
const poll = async () => {
const status = await gordoGet(`/research/backtest/${job.job_id}`);
console.log(`Status: ${status.status} Progress: ${status.progress || 0}%`);
if (!["completed", "failed"].includes(status.status)) {
setTimeout(poll, 3000);
} else {
const results = await gordoGet(`/research/backtest/${job.job_id}/results`);
console.log(`Sharpe: ${results.sharpe_ratio}`);
}
};
poll();
/v1/research/optimize
Requires full scope
Submit a Bayesian optimization job. Uses Optuna TPE sampler for sample-efficient parameter search.
Request Body
{
"template_name": "mean_reversion",
"param_names": ["lookback_period", "zscore_threshold"],
"n_trials": 50,
"objective": "sharpe_ratio",
"days": 180,
"use_bayesian": true
}
/v1/research/experiments
/v1/research/experiments/{experiment_id}
List or get detail for research experiments (backtests, optimizations, walk-forwards).
Webhooks
Webhooks let you receive real-time notifications when events occur in your Gordo account. All webhook payloads are signed with HMAC-SHA256 for verification.
/v1/webhooks/
Requires full scope
List all configured webhooks.
/v1/webhooks/
Requires full scope
Create a webhook. Returns the HMAC secret for signature verification (shown once).
Request Body
{
"name": "My Discord Bot",
"url": "https://my-server.com/gordo-webhook",
"events": ["signal.created", "order.filled", "risk.event"]
}
import hmac, hashlib
# Create a webhook
resp = requests.post(f"{BASE}/webhooks/",
headers={"X-API-Key": API_KEY},
json={
"name": "My Trading Alerts",
"url": "https://my-server.com/gordo-webhook",
"events": ["signal.created", "order.filled"]
})
webhook = resp.json()
print(f"Webhook ID: {webhook['id']}")
print(f"Secret (save this!): {webhook['secret']}")
# Verify webhook signatures in your receiver:
def verify_signature(payload_bytes, signature, secret):
expected = hmac.new(secret.encode(), payload_bytes,
hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
import crypto from "crypto";
// Create a webhook
const webhook = await fetch(`${BASE}/webhooks/`, {
method: "POST",
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
name: "My Trading Alerts",
url: "https://my-server.com/gordo-webhook",
events: ["signal.created", "order.filled"],
}),
}).then(r => r.json());
console.log(`Secret: ${webhook.secret}`); // Save this!
// Verify signatures in your Express handler:
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return signature === `sha256=${expected}`;
}
/v1/webhooks/{webhook_id}
Requires full scope
Update webhook URL, name, events, or active status.
/v1/webhooks/{webhook_id}
Requires full scope
Delete a webhook.
/v1/webhooks/{webhook_id}/deliveries
Get delivery log for a webhook — status codes, response times, and any errors.
Webhook Event Types
| Event | Description |
|---|---|
signal.created | A strategy emitted a new signal (entry, exit, flatten) |
order.created | An order was created from a signal |
order.filled | An order was fully filled on the exchange |
order.rejected | An order was rejected (risk check, exchange error) |
position.opened | A new position was opened |
position.closed | A position was fully closed |
risk.event | A risk event occurred (breach, throttle, guard) |
strategy.promoted | A strategy was promoted (paper → canary → live) |
strategy.demoted | A strategy was demoted or rolled back |
regime.changed | The market regime classification changed |
Webhook Payload Format
{
"event": "signal.created",
"timestamp": "2026-02-27T08:15:30Z",
"data": {
"signal_id": 1234,
"action": "long_entry",
"strategy": "mean_reversion_v2",
"package_id": 5,
"price": 84500.00,
"confidence": 0.82
}
}
Signature Header
Each delivery includes an X-Gordo-Signature header with
sha256={hmac_hex_digest}. Verify this against the raw request body
using your webhook secret.