API reference
Two endpoints, one key.
Everything your bot needs to trade through Candleweb. Read the market data endpoint, post a decision back, and we handle the order from there.
Base URL
All requests go to a single host over HTTPS. There is no sandbox host — use a trade saved as paused to exercise the loop without placing orders.
https://api.candleweb.io/v1
Authentication
Every request carries your developer key in a header. The same key travels in both directions: we send it when we call your bot, so you can verify the candle came from us; your bot sends it back with a decision, which is what authorises us to place the order.
X-Candleweb-Key: cw_dev_8f3a91c47e2b6d05af1e34c9Keys are issued per bot. Regenerating one takes effect immediately, so update your workflow first. Treat it like a password — anyone holding it can send decisions that trade your account.
The trading loop
A trade is a configuration you create in the dashboard: pair, timeframe, indicators, risk and size. A bot is what decides. Every endpoint below is scoped to a trade, so editing the trade changes what the endpoint returns without touching your workflow.
candle closes → Candleweb computes your indicators → your bot reads them (GET /candles) or receives a push → your bot replies (POST /decision) → Candleweb places the order, arms the stop, trails it, closes it → the whole thing is written to your trade records
Get market data
GET /v1/trades/{trade_id}/candlesReturns the closed candles and indicator values for this trade. Safe to poll; the payload only changes when a candle closes.
| Query parameter | Type | Description |
|---|---|---|
| limit | integer | Candles to return. Defaults to the trade's history setting. Max 200. |
| format | string | full (default) or compact, which drops the OHLCV array and returns indicators only. |
{
"trade_id": "tr_9f2c",
"pair": "BTC/USDT",
"timeframe": "15m",
"closed_at": "2026-08-22T14:45:00Z",
"candles": [
{ "t": "14:30:00Z", "o": 64180.2, "h": 64221.4, "l": 64150.0, "c": 64210.5, "v": 489 },
{ "t": "14:45:00Z", "o": 64210.5, "h": 64388.0, "l": 64180.2, "c": 64355.9, "v": 533 }
],
"indicators": {
"rsi_14": 61.4,
"macd_12_26_9": 18.7,
"ema_50": 63980.1,
"atr_14": 214.6
},
"position": null
}position is null when nothing is open, or an object describing the current position — useful if your bot's decision depends on whether it's already in the market.
Post a decision
POST /v1/trades/{trade_id}/decision| Field | Type | Description |
|---|---|---|
| actionrequired | string | buy, sell, hold, or close to exit an open position. |
| size_pct | number | Percentage of balance to commit. Capped by the trade's ceiling; ignored on hold and close. |
| rationale | string | One sentence explaining the decision. Stored with the record and read by bot review. Strongly recommended. |
| confidence | number | Optional, 0–1. Recorded but not acted on. |
// request { "action": "buy", "size_pct": 10, "rationale": "RSI recovering off 45, MACD positive, above EMA-50." } // response — 201 { "decision_id": "dc_4a71e0", "accepted": true, "order": { "side": "buy", "qty": 0.0311, "filled_at": 64356.10, "stop": 63712.00, "size_pct_applied": 8.3 } }
size_pct_applied may be lower than you asked for — that's your risk setting and position ceiling being enforced. If the order is refused entirely, accepted is false and a reason field explains why.
Receive a push
If you chose push when adding your bot, we POST the same payload as GET /candles to your URL at every close, carrying your developer key. Reply with a decision body and we'll act on it — no separate POST needed.
// what we send you POST https://your-n8n.app/webhook/candleweb X-Candleweb-Key: cw_dev_8f3a91c47e2b6d05af1e34c9 // what we expect back, within your timeout { "action": "hold", "rationale": "RSI 74 — overbought, waiting." }
Verify the key before you act on the payload. If you don't reply in time we apply the no-reply rule you set on the bot — skip, hold, or close — and log the miss.
Read your state
Three read-only endpoints for building your own views. All paginated, all scoped to your account.
| Endpoint | Returns |
|---|---|
| GET /v1/trades | Your trades and their current status. |
| GET /v1/positions | Open positions with entry, stop and unrealised PnL. |
| GET /v1/records | Closed trades, each with the market data and rationale behind it. |
Indicators
Selected per trade in the dashboard. The key in the payload is always the lowercase name with its parameters, so ema_50 and macd_12_26_9 are stable and predictable.
| Key | Indicator |
|---|---|
| rsi_14 | Relative Strength Index |
| macd_12_26_9 | MACD histogram value |
| ema_50 · ema_200 | Exponential moving averages |
| atr_14 | Average True Range |
| bb_20_2 | Bollinger Bands — upper, middle, lower |
| stoch_14_3 | Stochastic oscillator |
| vol_sma_20 | Volume, 20-period average |
Errors
Standard HTTP codes with a JSON body. Anything we refuse is written to your records with the reason, so a rejected order is never silent.
| Code | Meaning |
|---|---|
| 401 | Missing or invalid developer key. |
| 403 | Key is valid but not authorised for this trade. |
| 404 | No such trade, or it belongs to another account. |
| 409 | Decision conflicts with current state — a buy while already long, for example. |
| 422 | Order refused by your own limits. Check reason. |
| 429 | Rate limited. Back off and retry. |
| 502 | The exchange rejected or timed out. We retry; the record shows what happened. |
Rate limits
Limits are per account, not per exchange — we compute indicators centrally so you never touch the exchange's own limits. Current headroom is returned on every response.
X-RateLimit-Remaining: 4871 X-RateLimit-Reset: 1756483200
n8n quickstart
Two nodes. Set the HTTP Request node's header, point it at your trade, feed the result into whatever decides, and post the answer back.
// node 1 — HTTP Request (GET) URL https://api.candleweb.io/v1/trades/tr_9f2c/candles Header X-Candleweb-Key: cw_dev_... // node 2 — HTTP Request (POST) URL https://api.candleweb.io/v1/trades/tr_9f2c/decision Header X-Candleweb-Key: cw_dev_... Body {{ $json.decision }}
There's a full walkthrough with screenshots on the blog, and the Academy covers building the decision step from scratch.