Gordo Trader API

Programmatic access to your Gordo Trader account. Manage strategies, monitor positions, access market data, and automate your trading workflow.

Base URL
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
Security: Keep your API keys secret. Never expose them in client-side code, public repositories, or browser requests. If a key is compromised, revoke it immediately from your Settings page.

Rate Limits

Rate limits depend on your subscription tier. Limits are applied per API key.

Tier Requests / Minute Requests / Day
Free301,000
Basic6010,000
Pro12050,000
Enterprise300200,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.

CodeMeaning
200Success
201Created
400Bad request — check your parameters
401Unauthorized — missing or invalid API key
403Forbidden — insufficient scope
404Not found
429Rate limit exceeded
500Internal 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

GET /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"
}
GET /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

GET /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"
}
GET /v1/market/candles

Get OHLCV candle data. Supports multiple intervals aggregated from 1-minute base data.

Parameters

ParamTypeDefaultDescription
intervalstring5m1m, 5m, 15m, 30m, 1h, 4h, 1d
limitint500Number 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
  }
]
GET /v1/market/funding

Get funding rate history for BTC perpetual futures.

Parameters

ParamTypeDefaultDescription
hoursint168Lookback in hours (max 8760)
limitint500Max 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}`)
);
GET /v1/market/open-interest

Get open interest snapshots showing aggregate positioning.

Parameters

ParamTypeDefaultDescription
hoursint168Lookback in hours (max 8760)
limitint500Max records (max 5000)
coinstringBTCCoin symbol
GET /v1/market/liquidations

Get detected liquidation burst events — aggregated clusters of liquidation activity.

Parameters

ParamTypeDefaultDescription
hoursint168Lookback in hours
limitint100Max records (max 500)
GET /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

GET /v1/positions

List all open positions across your strategies.

Parameters

ParamTypeDefaultDescription
modestringallpaper, 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}`)
);
GET /v1/positions/{position_id}

Get full details for a single position, including linked signals and fills.

POST /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 }
GET /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
}
GET /v1/portfolio/equity

Get equity curve snapshots over time.

Parameters

ParamTypeDefaultDescription
daysint30Lookback in days (max 365)
snapshot_typestringdailyhourly or daily

Orders & Fills

GET /v1/orders

List order history.

Parameters

ParamTypeDefaultDescription
limitint100Max records (max 500)
hoursint24Lookback in hours (max 8760)
modestringallpaper, live, canary
statusstringallpending, filled, cancelled
package_idintallFilter by strategy package
GET /v1/orders/{order_id}

Get order details with associated fills.

GET /v1/fills

List fill history.

Parameters

ParamTypeDefaultDescription
limitint100Max records (max 500)
hoursint24Lookback in hours (max 8760)

Signals

GET /v1/signals/

List signal history — entry, exit, and flatten signals emitted by your strategies.

Parameters

ParamTypeDefaultDescription
limitint100Max records (max 500)
hoursint168Lookback in hours
package_idintallFilter by strategy
actionstringalllong_entry, short_entry, exit, flatten
GET /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}`)
);
GET /v1/signals/{signal_id}

Get full signal detail including metadata and strategy context.

Strategies

GET /v1/strategies/

List all your strategy packages.

Parameters

ParamTypeDefaultDescription
active_onlyboolfalseOnly return active strategies
include_archivedboolfalseInclude 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}`)
);
GET /v1/strategies/{package_id}

Get full strategy details including performance stats, current position, and promotion history.

POST /v1/strategies/{package_id}/pause
POST /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 },
});
PATCH /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
  }
}
GET /v1/strategies/templates

List available strategy templates with their parameter schemas.

GET /v1/strategies/deployments

Get promotion/rollback history across all strategies.

Parameters

ParamTypeDefaultDescription
limitint50Max records

Analytics

GET /v1/analytics/trades

Get reconstructed round-trip trades with entry/exit prices, P&L, and duration.

Parameters

ParamTypeDefaultDescription
limitint50Max trades (max 500)
daysint90Lookback in days (max 365)
modestringallpaper, 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"
  }
]
GET /v1/analytics/stats

Get aggregate trade statistics for a given period.

Parameters

ParamTypeDefaultDescription
daysint90Lookback (max 365)
modestringallpaper, 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
}
GET /v1/analytics/daily-pnl

Get daily P&L breakdown from equity snapshots.

Parameters

ParamTypeDefaultDescription
daysint30Lookback (max 365)
GET /v1/analytics/notes
POST /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"],
  }),
});
GET /v1/analytics/export

Export trade data as JSON or CSV.

Parameters

ParamTypeDefaultDescription
daysint90Lookback
formatstringjsonjson 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

GET /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
}
GET /v1/risk/events

Get risk event log (breaches, throttling, guard activations).

Parameters

ParamTypeDefaultDescription
limitint50Max events (max 500)
hoursint168Lookback (max 8760)
severitystringallinfo, warning, critical
GET /v1/risk/limits
PATCH /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
}
POST /v1/risk/kill-switch

Requires trade scope

Toggle the emergency kill switch. When active, all trading is halted immediately.

This is a toggle — calling it once activates the kill switch, calling again deactivates it. Always check /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

POST /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();
POST /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
}
GET /v1/research/experiments
GET /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.

GET /v1/webhooks/

Requires full scope

List all configured webhooks.

POST /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}`;
}
PATCH /v1/webhooks/{webhook_id}

Requires full scope

Update webhook URL, name, events, or active status.

DELETE /v1/webhooks/{webhook_id}

Requires full scope

Delete a webhook.

GET /v1/webhooks/{webhook_id}/deliveries

Get delivery log for a webhook — status codes, response times, and any errors.

Webhook Event Types

EventDescription
signal.createdA strategy emitted a new signal (entry, exit, flatten)
order.createdAn order was created from a signal
order.filledAn order was fully filled on the exchange
order.rejectedAn order was rejected (risk check, exchange error)
position.openedA new position was opened
position.closedA position was fully closed
risk.eventA risk event occurred (breach, throttle, guard)
strategy.promotedA strategy was promoted (paper → canary → live)
strategy.demotedA strategy was demoted or rolled back
regime.changedThe 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.