Install
openclaw skills install @psyb0t/ibkr-httpapiHTTP+JSON control plane over Interactive Brokers (ib_async + a local IB Gateway container) that the user already runs. Talk to a real brokerage with curl + JSON — market data (OHLC bars, snapshot/historical ticks, option chains + Greeks) across stocks/options/futures/cfd/forex/crypto, account/positions summaries, order placement / retrieval / cancellation, and server-side technical analysis via the wickworks sidecar. Bearer-token auth (API_TOKEN). Use when the user has deployed ibkr-httpapi and set IBKR_HTTPAPI_URL and wants to pull IBKR market data, inspect account/positions, run TA, or place/cancel orders. THIS API CAN PLACE, EXERCISE, AND CANCEL REAL ORDERS ON A REAL IBKR ACCOUNT — every account-mutating call requires explicit per-action user confirmation.
openclaw skills install @psyb0t/ibkr-httpapiHTTP client for an IBKR bridge the user has already deployed. It wraps Interactive Brokers via ib_async and a Linux-native IB Gateway container, and exposes a curl-friendly JSON surface: contract details, snapshot + historical ticks, OHLC bars, option chains with Greeks, account/positions summaries, cross-asset order entry, and server-side TA via the wickworks sidecar. Same operational shape as its sister project mt5-httpapi — one bridge, one token, same asset-class routing model.
This skill talks to a running ibkr-httpapi server. It does not stand one up, does not provision IBKR credentials, and does not place trades on its own initiative.
Destructive & irreversible. POST /orders places a REAL order on a real
IBKR brokerage account — it moves real money and creates live market
activity with no undo. DELETE /orders/{orderId} and DELETE /orders
cancel real orders; POST /options/exercise exercises/lapses real option
contracts. An agent must NEVER call any of these unless the user explicitly
requested that exact action with the specific parameters (symbol, side,
quantity, price) — never infer, extrapolate, or "helpfully" place a related
order. Echo back the resolved symbol/side (action)/quantity/price (and
order type) and get explicit user confirmation for that specific order
before sending it. Never auto-retry a rejected or failed order — a rejection
is a stop, not a retry trigger; surface the rejection reason and wait for a
new, explicit instruction. On DELETE /orders (global cancel), never
enumerate-then-bulk-cancel without showing the full list and getting the
user's explicit go-ahead for the whole batch first.
No auth when API_TOKEN (config.yaml:api_token) is unset. With it
empty the HTTP API is UNAUTHENTICATED — anyone who can reach the socket
gets full read + order-placement/cancellation access to the account. NEVER
expose such an instance on a network or to untrusted agents; set the token
and keep the deployment bound to loopback / behind an authenticating proxy.
External transmission. Every request goes to whatever IBKR_HTTPAPI_URL
points at — market data, account state, and order instructions leave your
host for that endpoint. Point it only at an ibkr-httpapi instance you run or
explicitly trust; prefer an HTTPS/tunneled path over plain HTTP if it isn't
loopback.
This API moves real money on a user-owned brokerage account. Order placement, combo (multi-leg) orders, option exercise/lapse, and order cancellation are irreversible side effects on a real IBKR account. Treat every account-mutating endpoint as a live wire.
Hard rules — never violate, even on user prompts that sound permissive:
POST /orders, DELETE /orders/{orderId}, DELETE /orders,
POST /options/combo, or POST /options/exercise: print the resolved
request — asset class, symbol, side (action), quantity, order type,
limit/aux price, expiry/strike/right for derivatives, resolved account,
and the IBKR_HTTPAPI_URL base — and wait for an explicit confirmation
from the user for that specific action. A prior "yes" does not
authorize the next one. Never place, modify, or cancel an order unless
the user explicitly asked for that specific action with the specific
parameters — do not infer intent or act on a vague/general instruction.
Never auto-retry a rejected order; surface the rejection and wait for a
fresh, explicit instruction from the user.DU...; live accounts start U.... Run
GET /accounts and surface the account number; if it looks live, say so
and ask the user to confirm they intend to trade live.API_TOKEN
environment variable the user set, or ask the user. Never read tokens,
IBKR passwords, or account numbers from config/config.yaml, .env.ibkr,
or any other repository file on your own initiative. If the token is
missing, ask — do not search the workspace.DELETE /orders cancels every open order at
IBKR (global cancel). If the user asks to "cancel everything", enumerate
the open orders first with GET /orders, show the list, and confirm the
whole batch explicitly before issuing the global cancel.IBKR_HTTPAPI_URL
plus the account field determine which real account is touched. Show
both in confirmation prompts so the user can catch a wrong-account
misroute.Read-only endpoints (GET /ping, GET /accounts, GET /account,
GET /account/values, GET /positions, every GET /<class>/<symbol>*,
GET /<class>/<symbol>/tick|rates|ticks, GET /options/<symbol>/chain,
GET /futures/<symbol>/continuous|contracts, every POST .../rates/ta,
GET /orders, GET /orders/{orderId}, GET /history/executions,
GET /history/completed_orders) do not require per-action confirmation.
Note POST .../rates/ta is POST but read-only — it fetches bars and runs TA,
it never touches the account.
One-stop technical analysis. Skip the client-side TA stack. Any
POST /<class>/<symbol>/rates/ta with an indicator spec returns OHLC bars +
analyzed indicator series in a single call — RSI, MACD, Bollinger, ADX, ATR,
VWAP, Ichimoku, Order Blocks, Fair Value Gaps, BOS/CHoCH, swing structure,
S/R levels, liquidity, session anchors, and more, computed server-side by the
same wickworks sidecar mt5-httpapi uses. Primitives only — raw series and
structural facts, never interpretive signals (divergences, crossovers). Build
those in the consumer. See API — TA enrichment.
For installation, configuration, and container setup, see references/setup.md.
The server also speaks MCP (streamable-HTTP)
at /mcp, exposing the REST surface as dedicated typed tools whose names +
params + descriptions are the agent's documentation. The 6 asset classes
collapse behind an asset_class enum: market data (get_contract, get_quote,
get_rates, get_rates_ta, get_stock_ticks), specials (get_option_chain,
place_option_combo, exercise_option, get_future_continuous,
list_future_contracts), orders (list_orders, get_order, place_order,
cancel_order, cancel_all_orders), and account/positions/history
(get_account, get_account_values, list_accounts, list_positions,
get_executions, get_completed_orders, ping). A generic request +
endpoints catalog remain as a fallback. Same bearer auth as REST — see
Bearer / Token Auth; the order-placement / cancellation /
exercise tools are irreversible live-account actions. Connect straight at
$IBKR_HTTPAPI_URL/mcp (the app root, not the /v1 REST prefix) or via the
@psyb0t/ibkr-httpapi
OpenClaw plugin for stdio-only MCP clients.
IBKR_HTTPAPI_URL.rates/ta.IBKR_HTTPAPI_URL —
this is not a generic "get me a stock quote" tool.DELETE /orders/{orderId}, confirmed) and place
a new one (POST /orders, confirmed).make targets on the host (see setup).The container should already be running. Point at it (the base URL must
include the fixed /v1 prefix — the runtime serves every path under /v1):
export IBKR_HTTPAPI_URL=http://localhost:8889/v1
If the server has api_token / API_TOKEN configured, export the token too:
export API_TOKEN=<the-token-the-user-gives-you>
# every request below needs: -H "Authorization: Bearer $API_TOKEN"
Only accept the token from the API_TOKEN env var the user set, or from the
user directly. Never read it from config/config.yaml, .env.ibkr, or any
other file in the workspace. If IBKR_HTTPAPI_URL is unset, ask the user —
do not discover it from project files.
Verify:
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/ping"
# {"status":"ok","connected":true,"serverVersion":...,"uptimeSeconds":...}
connected: true means the upstream IB Gateway socket is alive. connected: false means the API is up but the gateway is disconnected (nightly IBKR
restart, 2FA prompt, or gateway still booting) — market-data and order calls
will fail with 502/503 until it reconnects.
Auth is optional server-side. If no token is configured, requests go through
without one; if a token is set, every route (including /ping) requires
Authorization: Bearer <token> and returns 401 without it. From the
agent's side, never assume the server is auth-disabled — always pass the token
the user provided if there is one.
GET reads, POST creates (orders, combos, exercise) or runs TA, DELETE
cancels. All request/response bodies are JSON. Asset class is the URL prefix
and every per-symbol endpoint has the same shape across classes:
GET /<class>/<symbol> — contract detailsGET /<class>/<symbol>/tick — snapshot bid/ask/last (+ Greeks on options)GET /<class>/<symbol>/rates — historical OHLC barsGET /<class>/<symbol>/ticks — historical raw ticks (stocks only, currently)POST /<class>/<symbol>/rates/ta — bars + wickworks TAClasses: /stocks, /options, /futures, /cfd, /forex (path segment is
{pair}, e.g. EURUSD), /crypto.
Error envelope — every error is:
{ "code": "UPPER_SNAKE_CASE", "message": "human readable", "details": { } }
details is optional. Status codes: 400 bad input, 401 missing/bad token,
404 unknown contract/order/account, 429 RATE_LIMIT_NEAR (preemptive
pacing gate — back off and retry, IBKR access stays intact), 502 upstream
(IB Gateway / wickworks) failure, 503 dependency down (gateway disconnected
or wickworks URL unset).
Caching + ?refresh. Cacheable read endpoints (/rates, /ticks,
rates/ta, /<class>/{symbol}, /options/{symbol}/chain,
/futures/{symbol}/continuous|contracts, /history/*) transparently read
from an on-disk cache and write results back. Pass ?refresh=true to bypass
the cache read and force a fresh upstream fetch (still written back). Default
false — caching is recommended.
| Prefix | Class | secType | Defaults | Required query params |
|---|---|---|---|---|
/stocks/{symbol} | Equities | STK | exchange=SMART, currency=USD | — |
/options/{symbol} | Options | OPT | exchange=SMART, currency=USD, multiplier=100 | expiry, strike, right |
/options/{symbol}/chain | Option chain | — | — | — (underlyingSecType=STK default) |
/futures/{symbol} | Futures | FUT | currency=USD | exchange and expiry |
/futures/{symbol}/continuous | Continuous future | CONTFUT | currency=USD | exchange |
/futures/{symbol}/contracts | All expiries | FUT | currency=USD | exchange |
/cfd/{symbol} | CFDs | CFD | exchange=SMART, currency=USD | — |
/forex/{pair} | Currencies | CASH | exchange=IDEALPRO | — |
/crypto/{symbol} | Crypto | CRYPTO | exchange=PAXOS, currency=USD | — |
Defaults come from config.yaml:contract_defaults.<class> on the server —
omit a field to use the default, or override it per request via the query
param. Futures must carry an explicit exchange (CME, NYMEX, GLOBEX, …)
— there is no default.
GET /pingLiveness + gateway connection state.
| Field | Type | Notes |
|---|---|---|
status | string | "ok" when the API is up |
connected | bool | upstream IB Gateway socket alive |
serverVersion | int? | TWS server version |
startTime, uptimeSeconds, msgsSent, msgsRecv | connection stats |
GET /accounts{ "accounts": ["DU1234567", ...] } — every account the login can trade.
Paper accounts start DU, live accounts start U.
GET /account and GET /account/valuesBoth take optional ?account=<id>. Return
{ "accounts": { "<id>": { "<Tag>": { "value": "...", "currency": "...", "modelCode": "..." }, ... } } }.
/account is the summary set (NetLiquidation, AvailableFunds, BuyingPower,
maintenance margin, …); /account/values is the fuller raw account-values
stream. Values are IBKR tag strings — parse value as needed.
GET /positionsOptional ?account=<id>. Returns
{ "positions": [ { "account", "contract": {conid, symbol, secType, exchange, currency, ...}, "position": <qty>, "avgCost": <number> } ] }.
GET /<class>/<symbol> returns { "contracts": [ { "contract": {...}, "minTick", "longName", "validExchanges": [...], "tradingHours", "liquidHours", "timeZoneId", ... } ] }.
GET /<class>/<symbol>/tickSnapshot ticker. Fields (all nullable): bid, bidSize, ask, askSize,
last, lastSize, open, high, low, close, volume, vwap,
halted, time, plus contract. On options with an OPRA subscription and
market_data_type: 1, modelGreeks / bidGreeks / askGreeks /
lastGreeks carry { delta, gamma, theta, vega, impliedVol, optPrice, undPrice }. With free delayed data (market_data_type: 3) Greeks may be
null.
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/stocks/AAPL/tick"
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/options/AAPL/tick?expiry=20260619&strike=200&right=C"
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/futures/ES/tick?expiry=202609&exchange=CME"
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/crypto/BTC/tick?currency=USD"
GET /<class>/<symbol>/ratesHistorical OHLC bars. Response:
{ "symbol"/"pair", "secType", "expiry"?, "strike"?, "right"?, "bars": [ { "time", "open", "high", "low", "close", "volume"?, "average"?, "barCount"? } ] }.
| Param | Required | Default | Notes |
|---|---|---|---|
duration | yes | — | IBKR duration string: 60 S / 1 D / 13 W / 6 M / 1 Y (URL-encode the space as + or %20) |
barSize | no | — | 1 min / 15 mins / 1 hour / 1 day etc. (IBKR bar-size strings) |
endDateTime | no | now | "" = now, or YYYYMMDD HH:MM:SS UTC |
whatToShow | no | — | TRADES / MIDPOINT / BID / ASK / ADJUSTED_LAST / … |
useRTH | no | false | regular trading hours only |
exchange, currency, primaryExchange | no | class defaults | contract overrides |
refresh | no | false | bypass cache read |
Options/futures rates also take the contract-selecting params (expiry,
strike, right for options; expiry + exchange for futures).
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/stocks/AAPL/rates?duration=1+Y&barSize=1+day&whatToShow=ADJUSTED_LAST"
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/forex/EURUSD/rates?duration=30+D&barSize=1+hour&whatToShow=MIDPOINT"
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/futures/ES/rates?expiry=202609&exchange=CME&duration=30+D&barSize=1+day"
GET /stocks/{symbol}/ticksHistorical raw ticks (equities). Response
{ "symbol", "secType", "ticks": [ { "time", "price"?, "size"?, "bid"?, "ask"?, "bidSize"?, "askSize"? } ] }.
| Param | Required | Default | Notes |
|---|---|---|---|
startDateTime / endDateTime | no | — | UTC window bounds |
numberOfTicks | no | 1000 | 1–1000 |
whatToShow | no | — | TRADES / BID_ASK / MIDPOINT |
useRTH | no | false | |
refresh | no | false |
GET /options/{symbol}/chainFull option chain. Params: underlyingSecType (default STK),
futFopExchange, underlyingConId, refresh. Response
{ "symbol", "underlyingConId"?, "chains": [ { "exchange", "tradingClass", "multiplier", "expirations": ["YYYYMMDD", ...], "strikes": [<number>, ...] } ] }.
GET /futures/{symbol}/continuous and .../contractsBoth require exchange. /continuous returns one continuous contract;
/contracts returns every expiry (?includeExpired=true to include expired).
Both return the ContractDetailsList shape ({ "contracts": [...] }).
POST /<class>/<symbol>/rates/taBars + wickworks TA in one round-trip. POST but read-only — no account
mutation. Contract-selecting params (expiry/strike/right,
exchange/currency) go in the query string; the bar window + indicator spec
go in the JSON body.
| Field | Required | Default | Notes |
|---|---|---|---|
duration | yes | — | IBKR duration string (200 D, 1 Y) |
barSize | no | — | IBKR bar-size string |
endDateTime | no | — | "" = now, or YYYYMMDD HH:MM:SS UTC |
whatToShow | no | — | TRADES / MIDPOINT / ADJUSTED_LAST / … |
useRTH | no | false | |
indicators | no | — | wickworks indicator spec (see below) |
recentBars | no | — | reserved; wickworks may ignore it |
Response: { "symbol"?, "secType"?, "bars": [ ... ], "ta": { <keys mirror your indicators object> }, "asOf"? }.
indicators shape — "name": true for defaults, or "name": {"length": 21, ...}
to tune (params flat on the object; add "type": "<name>" only when the
output key must differ from the indicator name, e.g. two RSIs as
rsi14/rsi21). Catalog (same wickworks build as mt5-httpapi):
ema sma hma wma dema tema t3 kama alma linreg jma zlma rma fwma swma sinwma trima vwma vwaprsi mfi willr cci roc mom uo stoch stochrsi macd tsi trix fisheradx aroon vortexatr natrobv ad cmf adosc kvobbands kc donchiansupertrend psar chandelierExit ichimokusqueezeorderBlocks fvg bosChoch swingLevels srLevels recentRange liquidity previousHighLow sessions retracementsprice levels momentum volume position slopeFull catalog with every param + output shape: github.com/psyb0t/docker-wickworks.
Match duration to your slowest indicator's lookback × ~2 for warmup (e.g.
sma(200) → fetch ≥ 400 bars). Under-feed and wickworks returns a
per-indicator deficit list. Returns 503 if the server has no wickworks URL
configured.
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
"$IBKR_HTTPAPI_URL/stocks/AAPL/rates/ta" \
-d '{
"duration": "200 D",
"barSize": "1 day",
"whatToShow": "ADJUSTED_LAST",
"indicators": { "rsi": true, "macd": true, "bbands": {"length": 20, "std": 2} }
}'
Every
POST /orders,POST /options/combo,POST /options/exercise,DELETE /orders/{orderId}, andDELETE /ordersopens, exercises, or cancels a real order on the user's IBKR account. Before invoking any of them you MUST: (1) print the full resolved request (base URL, resolvedaccount, asset class, symbol,action,quantity,orderType, prices, and expiry/strike/right for derivatives); (2) ask the user to confirm that specific action; (3) wait for an explicit yes. A prior confirmation does not carry over. Never place, modify, or cancel an order unless the user explicitly requested that specific action with the specific symbol/side/quantity/price — do not act on a vague instruction, and never place a "similar" or "adjusted" order the user didn't ask for. Never auto-retry a rejected order — a4xx/5xxor aTradewhoseorderStatus.statusshows a rejection is a stop; report the rejection reason to the user and wait for a fresh, explicit instruction before submitting anything again, even with the same parameters.
GET /orders and GET /orders/{orderId} (read-only)GET /orders → { "trades": [ Trade, ... ] }. GET /orders/{orderId} → one
Trade. A Trade is
{ "contract": {...}, "order": { orderId, permId, action, totalQuantity, orderType, lmtPrice?, auxPrice?, tif, ... }, "orderStatus": { status, filled, remaining, avgFillPrice, permId, ... }, "fills": [...], "log": [...] }.
POST /ordersPlace an order on any asset class. Body (OrderRequest):
| Field | Required | Default | Notes |
|---|---|---|---|
assetClass | yes | — | stock / option / future / cfd / forex / crypto |
symbol | yes | — | root symbol |
action | yes | — | BUY or SELL |
quantity | yes | — | > 0 |
orderType | no | MKT | MKT / LMT / STP / STP LMT / … (IBKR order types) |
lmtPrice | no | — | required for LMT |
auxPrice | no | — | stop trigger for stop orders |
tif | no | DAY | time-in-force |
outsideRth | no | false | allow fills outside regular hours |
transmit | no | true | false = stage without transmitting |
expiry / strike / right | for derivatives | — | option/future contract selectors |
exchange / currency / multiplier / tradingClass / primaryExchange | no | class defaults | contract overrides |
conid | no | — | pass a resolved conid to skip contract lookup |
account | no | server default | target account |
goodAfterTime / goodTillDate / ocaGroup / parentId | no | — | advanced routing |
Returns a Trade snapshot (check orderStatus.status). This places a REAL
order and moves real money — only call it for a symbol/side/quantity/price
the user explicitly asked for. Example (place ONLY after explicit
per-action confirmation):
# STOP: echo back "BUY 10 AAPL @ MKT" (or the LMT/STP price) to the user and
# get an explicit yes for THIS order before running the curl below.
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
"$IBKR_HTTPAPI_URL/orders" \
-d '{"assetClass":"stock","symbol":"AAPL","action":"BUY","quantity":10,"orderType":"MKT"}'
# Futures limit order — same rule: echo symbol/side/qty/price, confirm, then send.
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
"$IBKR_HTTPAPI_URL/orders" \
-d '{"assetClass":"future","symbol":"ES","expiry":"202609","exchange":"CME",
"action":"BUY","quantity":1,"orderType":"LMT","lmtPrice":5500.00}'
If the response shows a rejection (non-2xx, or orderStatus.status
indicating rejection/error), do not resubmit. Report the rejection to
the user and wait for a new explicit instruction — even a retry of the
identical order requires fresh confirmation.
DELETE /orders/{orderId} — cancel one{ "status": "cancelling", "orderId": <id> }. Confirmation required.
DELETE /orders — cancel ALL (global cancel){ "status": "global_cancel_requested" }. Cancels every open order.
Enumerate with GET /orders, show the list, confirm the whole batch.
POST /options/combo — multi-leg (BAG)Vertical / condor / butterfly / calendar. Body (ComboOrderRequest):
symbol (req), legs (req — array of { conid, ratio (default 1), action (BUY/SELL), exchange (default SMART) }), action (req), quantity (req),
orderType (default LMT), lmtPrice, tif (default DAY), exchange
(default SMART), currency (default USD), account. Returns a Trade.
Resolve leg conids from GET /options/{symbol}/chain + GET /options/{symbol}
first. Confirmation required.
# Vertical call spread — confirmed action only.
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
"$IBKR_HTTPAPI_URL/options/combo" \
-d '{"symbol":"AAPL",
"legs":[{"conid":681000001,"ratio":1,"action":"BUY"},
{"conid":681000002,"ratio":1,"action":"SELL"}],
"action":"BUY","quantity":1,"orderType":"LMT","lmtPrice":1.20}'
POST /options/exercise — exercise / lapseBody (ExerciseRequest): conid (req), action (req — EXERCISE or
LAPSE), quantity (req, ≥ 1), account (req), override (default false).
Returns { "status", "contract": {...}, "action" }. Exercise/lapse is
irreversible — confirmation required.
GET /history/executionsToday's fills. Optional filters: account, clientId, secType, symbol,
exchange, side, timeAfter (YYYYMMDD HH:MM:SS UTC), refresh. Returns
{ "fills": [ { "contract": {...}, "execution": {...}, "commissionReport": {...}?, "time" } ] }.
GET /history/completed_orders?apiOnly=false (default), ?refresh. Returns { "trades": [ {...}, ... ] }
(today + a historical buffer).
When api_token is set on the server (via config.yaml:api_token or the
API_TOKEN env), every route requires Authorization: Bearer <token>.
The token is compared in constant time (hmac.compare_digest); a
missing/wrong token returns 401 with the standard error envelope
(code: UNAUTHORIZED). Empty/unset token = wide open — any process that can
reach the socket can read account state and place orders, so on untrusted
networks the token is mandatory.
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/ping"
The agent obtains the token only from the API_TOKEN env var the user set,
or from the user directly — never by reading config.yaml / .env.ibkr /
workspace files.
# 1. Health.
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/ping" | jq
# 2. Contract sanity check.
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/stocks/AAPL" | jq '.contracts[0].contract'
# 3. Daily bars for the last year.
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/stocks/AAPL/rates?duration=1+Y&barSize=1+day&whatToShow=ADJUSTED_LAST" | jq '.bars | length'
# 4. Same bars + TA in one call.
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
"$IBKR_HTTPAPI_URL/stocks/AAPL/rates/ta" \
-d '{"duration":"400 D","barSize":"1 day","whatToShow":"ADJUSTED_LAST",
"indicators":{"rsi":true,"sma":{"length":200},"atr":true}}' | jq '.ta | keys'
# 5. Option chain → pick a strike → snapshot with Greeks.
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/options/AAPL/chain" | jq '.chains[0].expirations[:3]'
curl -s -H "Authorization: Bearer $API_TOKEN" \
"$IBKR_HTTPAPI_URL/options/AAPL/tick?expiry=20260619&strike=200&right=C" | jq '.modelGreeks'
# 1. Which account am I about to touch? (paper DU... vs live U...)
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/accounts" | jq
# 2. Account state + open positions/orders — context before mutating.
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/account" | jq
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/positions" | jq
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/orders" | jq
# 3. STOP. Print the resolved request (base URL, account, symbol, action,
# quantity, orderType, price) and get an explicit user "yes" for THIS order.
# 4. Only then place it.
curl -s -X POST -H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
"$IBKR_HTTPAPI_URL/orders" \
-d '{"assetClass":"stock","symbol":"AAPL","action":"BUY","quantity":10,
"orderType":"LMT","lmtPrice":180.00,"tif":"DAY"}' | jq '.orderStatus'
# 5. Confirm state; cancel needs its own confirmation.
curl -s -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/orders" | jq '.trades[].orderStatus.status'
# curl -s -X DELETE -H "Authorization: Bearer $API_TOKEN" "$IBKR_HTTPAPI_URL/orders/<orderId>"
/v1. Every path is served under the fixed /v1
prefix. IBKR_HTTPAPI_URL=http://localhost:8889/v1.exchange AND expiry. No exchange default for futures.rates/ta is POST but read-only — never account-mutating.429 RATE_LIMIT_NEAR is expected under load — the server gates
before crossing IBKR's pacing caps. Read details.retry_after_sec, back
off, retry. Don't hammer through it — repeat pacing violations can get
IBKR API access revoked.connected: false on /ping means the gateway socket is down (nightly
IBKR restart ~01:00 ET, weekly 2FA push, or still booting). Market-data and
order calls fail until it reconnects; retry after a short wait.market_data_type: 3 gives
15–20 min delayed bars and often null option Greeks. Real-time needs an
OPRA subscription + market_data_type: 1 on the server.?refresh=true forces a fresh upstream pull on any cacheable read; the
value is still cached for next time.