Quickstart
From an email address to a running strategy — every request shown in full.
The complete integration path, for a human at a terminal or an AI agent driving the API. Two moments need the account owner personally: relaying a six-digit code, and (for live trading) funding and authorizing their own wallet.
1. Register with your email#
curl -X POST https://api.superior.trade/account/register \
-H "content-type: application/json" \
-d '{"email":"operator@example.com"}'
wget -qO- \
--method=POST \
--header='content-type: application/json' \
--body-data='{"email":"operator@example.com"}' \
https://api.superior.trade/account/register$response = Invoke-RestMethod `
-Method POST `
-Uri "https://api.superior.trade/account/register" `
-Headers @{ "content-type" = "application/json" } `
-Body '{"email":"operator@example.com"}'
$response | ConvertTo-Json{ "status": "otp_sent", "expires_in": 600 }
Superior emails a six-digit code to the operator. Nothing else is in that email — no links, no dashboard required.
2. Verify the OTP, receive the API key#
The operator reads the code out; the agent (or you) exchanges it:
curl -X POST https://api.superior.trade/account/verify \
-H "content-type: application/json" \
-d '{"email":"operator@example.com","otp":"123456"}'
wget -qO- \
--method=POST \
--header='content-type: application/json' \
--body-data='{"email":"operator@example.com","otp":"123456"}' \
https://api.superior.trade/account/verify$response = Invoke-RestMethod `
-Method POST `
-Uri "https://api.superior.trade/account/verify" `
-Headers @{ "content-type" = "application/json" } `
-Body '{"email":"operator@example.com","otp":"123456"}'
$response | ConvertTo-Json{
"token": "…",
"user": { "id": "…", "email": "operator@example.com" },
"api_key": "st_live_4f2a9c81d7e3b0…"
}
The api_key is returned once, in this response, never by email. Store it; every request from here on sends it as x-api-key.
3. Discover what you can trade#
curl https://api.superior.trade/context/venues \ -H "x-api-key: st_live_…"
wget -qO- \ --header='x-api-key: st_live_…' \ https://api.superior.trade/context/venues
$response = Invoke-RestMethod `
-Method GET `
-Uri "https://api.superior.trade/context/venues" `
-Headers @{ "x-api-key" = "st_live_…" }
$response | ConvertTo-JsonThe response lists every venue, which frameworks run on it, what credentials it needs, and its minimum deposit. Drill into instruments and history with /context/markets and /context/candles.
4. Backtest before anything touches capital#
This strategy is complete — it pastes and runs as-is (JSON-escape the newlines when embedding it in the request body):
from freqtrade.strategy import IStrategy
import talib.abstract as ta
class BtcRange(IStrategy):
timeframe = "4h"
minimal_roi = {"0": 0.06}
stoploss = -0.03
startup_candle_count = 30
def populate_indicators(self, dataframe, metadata):
bb = ta.BBANDS(dataframe["close"], timeperiod=20)
dataframe["bb_lower"] = bb["lowerband"]
dataframe["bb_upper"] = bb["upperband"]
dataframe["rsi"] = ta.RSI(dataframe["close"], timeperiod=14)
return dataframe
def populate_entry_trend(self, dataframe, metadata):
dataframe.loc[
(dataframe["close"] < dataframe["bb_lower"]) & (dataframe["rsi"] < 30),
"enter_long",
] = 1
return dataframe
def populate_exit_trend(self, dataframe, metadata):
dataframe.loc[dataframe["close"] > dataframe["bb_upper"], "exit_long"] = 1
return dataframe
curl -X POST https://api.superior.trade/runtime/backtests \
-H "x-api-key: st_live_…" \
-H "content-type: application/json" \
-d '{
"framework": "freqtrade",
"venue": "hyperliquid",
"symbols": ["BTC/USDC:USDC"],
"code": "<the BtcRange strategy above>",
"config": {
"timeframe": "4h",
"stake_amount": 100,
"max_open_trades": 1,
"exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] }
},
"range": { "from": "2026-01-01", "to": "2026-06-30" }
}'
wget -qO- \
--method=POST \
--header='x-api-key: st_live_…' \
--header='content-type: application/json' \
--body-data='{
"framework": "freqtrade",
"venue": "hyperliquid",
"symbols": ["BTC/USDC:USDC"],
"code": "<the BtcRange strategy above>",
"config": {
"timeframe": "4h",
"stake_amount": 100,
"max_open_trades": 1,
"exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] }
},
"range": { "from": "2026-01-01", "to": "2026-06-30" }
}' \
https://api.superior.trade/runtime/backtests$response = Invoke-RestMethod `
-Method POST `
-Uri "https://api.superior.trade/runtime/backtests" `
-Headers @{ "x-api-key" = "st_live_…"; "content-type" = "application/json" } `
-Body '{
"framework": "freqtrade",
"venue": "hyperliquid",
"symbols": ["BTC/USDC:USDC"],
"code": "<the BtcRange strategy above>",
"config": {
"timeframe": "4h",
"stake_amount": 100,
"max_open_trades": 1,
"exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] }
},
"range": { "from": "2026-01-01", "to": "2026-06-30" }
}'
$response | ConvertTo-JsonThe symbols and config.exchange.pair_whitelist values are the same exact framework_symbols.freqtrade alias returned by GET /context/markets. You may omit symbols and let the runtime derive it from pair_whitelist; when both are sent, they must match exactly.
Poll GET /runtime/backtests/:id until status is finished; the result — including market_change_pct, what buy-and-hold did — is embedded in the same response.
5. Deploy paper, and start it#
Paper mode needs no credentials and no funds. Creating a deployment does not start it — starting is always an explicit action:
curl -X POST https://api.superior.trade/runtime/deployments \
-H "x-api-key: st_live_…" \
-H "content-type: application/json" \
-d '{
"framework": "freqtrade",
"venue": "hyperliquid",
"mode": "paper",
"name": "btc-range-paper",
"code": "<the same strategy>",
"config": { "timeframe": "4h", "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] } }
}'
wget -qO- \
--method=POST \
--header='x-api-key: st_live_…' \
--header='content-type: application/json' \
--body-data='{
"framework": "freqtrade",
"venue": "hyperliquid",
"mode": "paper",
"name": "btc-range-paper",
"code": "<the same strategy>",
"config": { "timeframe": "4h", "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] } }
}' \
https://api.superior.trade/runtime/deployments$response = Invoke-RestMethod `
-Method POST `
-Uri "https://api.superior.trade/runtime/deployments" `
-Headers @{ "x-api-key" = "st_live_…"; "content-type" = "application/json" } `
-Body '{
"framework": "freqtrade",
"venue": "hyperliquid",
"mode": "paper",
"name": "btc-range-paper",
"code": "<the same strategy>",
"config": { "timeframe": "4h", "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] } }
}'
$response | ConvertTo-Jsoncurl -X PUT https://api.superior.trade/runtime/deployments/dep_01j8xv…/status \
-H "x-api-key: st_live_…" \
-H "content-type: application/json" \
-d '{ "action": "start" }'
wget -qO- \
--method=PUT \
--header='x-api-key: st_live_…' \
--header='content-type: application/json' \
--body-data='{ "action": "start" }' \
https://api.superior.trade/runtime/deployments/dep_01j8xv…/status$response = Invoke-RestMethod `
-Method PUT `
-Uri "https://api.superior.trade/runtime/deployments/dep_01j8xv…/status" `
-Headers @{ "x-api-key" = "st_live_…"; "content-type" = "application/json" } `
-Body '{ "action": "start" }'
$response | ConvertTo-JsonNow watch it behave with GET /runtime/deployments/:id/metrics — paper metrics carry "simulated": true so rehearsal numbers can never be mistaken for real ones.
6. Go live#
Before live trading, fund the managed wallet above the venue's min_deposit_usd. Then create the live deployment (mode is fixed at creation — live is a new deployment) and start it. Deployment creation does not pre-validate Freqtrade strategy code, so backtest it first and use deployment status and logs to diagnose startup failures. BYOK with a scoped trading key is coming soon.
curl -X POST https://api.superior.trade/runtime/deployments \
-H "x-api-key: st_live_…" \
-H "content-type: application/json" \
-d '{
"framework": "freqtrade",
"venue": "hyperliquid",
"mode": "live",
"name": "btc-range-live",
"code": "<the same strategy>",
"config": { "timeframe": "4h", "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] } },
"credentials": { "type": "managed" }
}'
wget -qO- \
--method=POST \
--header='x-api-key: st_live_…' \
--header='content-type: application/json' \
--body-data='{
"framework": "freqtrade",
"venue": "hyperliquid",
"mode": "live",
"name": "btc-range-live",
"code": "<the same strategy>",
"config": { "timeframe": "4h", "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] } },
"credentials": { "type": "managed" }
}' \
https://api.superior.trade/runtime/deployments$response = Invoke-RestMethod `
-Method POST `
-Uri "https://api.superior.trade/runtime/deployments" `
-Headers @{ "x-api-key" = "st_live_…"; "content-type" = "application/json" } `
-Body '{
"framework": "freqtrade",
"venue": "hyperliquid",
"mode": "live",
"name": "btc-range-live",
"code": "<the same strategy>",
"config": { "timeframe": "4h", "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] } },
"credentials": { "type": "managed" }
}'
$response | ConvertTo-JsonCheck readiness before starting — the deployment GET reports both credential and funding state, so there's no start-and-hope:
curl https://api.superior.trade/runtime/deployments/dep_01j8xw… \
-H "x-api-key: st_live_…"
# → "credentials": { "attached": true, "type": "managed", "status": "ready" },
# "venue_account": { "funded": true, "balance_usd": 141.20 }
wget -qO- \ --header='x-api-key: st_live_…' \ https://api.superior.trade/runtime/deployments/dep_01j8xw…
$response = Invoke-RestMethod `
-Method GET `
-Uri "https://api.superior.trade/runtime/deployments/dep_01j8xw…" `
-Headers @{ "x-api-key" = "st_live_…" }
$response | ConvertTo-Jsoncurl -X PUT https://api.superior.trade/runtime/deployments/dep_01j8xw…/status \
-H "x-api-key: st_live_…" \
-H "content-type: application/json" \
-d '{ "action": "start" }'
wget -qO- \
--method=PUT \
--header='x-api-key: st_live_…' \
--header='content-type: application/json' \
--body-data='{ "action": "start" }' \
https://api.superior.trade/runtime/deployments/dep_01j8xw…/status$response = Invoke-RestMethod `
-Method PUT `
-Uri "https://api.superior.trade/runtime/deployments/dep_01j8xw…/status" `
-Headers @{ "x-api-key" = "st_live_…"; "content-type" = "application/json" } `
-Body '{ "action": "start" }'
$response | ConvertTo-JsonIf something's missing, the error names it precisely — credentials_missing and account_not_funded are different problems with different fixes, and the error body carries a docs_url to the page that solves it.
That's the whole surface a working integration needs: two account calls, two context reads, a backtest, two deployments. Everything else in these docs is depth on those steps.