{"openapi":"3.1.0","info":{"title":"Vanta Trading API","version":"1.5.1","summary":"Programmatic trading for Vanta prop accounts.","description":"REST endpoints for reading account state and placing orders on a\nVanta prop account. Keys are created in the dashboard under\nSettings → Key Management and are bound to a single account.\n\nEvery request carries four headers. The signature is\nHMAC-SHA256, base64-encoded, over the newline-joined canonical string:\n\n    v1\\n<METHOD>\\n<path + query>\\n<timestampMs>\\n<nonce>\\n<sha256hex(body)>\n\nThe timestamp must be within 5 minutes of server time and each nonce may be used\nonly once per key. See /docs/authentication for worked examples.","termsOfService":"https://www.vantatrading.io/terms-of-service"},"externalDocs":{"description":"Full documentation","url":"https://staging.app.vantatrading.io/docs"},"servers":[{"url":"https://staging.app.vantatrading.io","description":"Production"}],"tags":[{"name":"Trading","description":"Account state and order placement."},{"name":"Keys","description":"The API key that signs the request."}],"security":[{"VantaKeyId":[],"VantaTimestamp":[],"VantaNonce":[],"VantaSignature":[]}],"paths":{"/api/v1/trading/account":{"get":{"operationId":"getAccount","summary":"Account snapshot","description":"Consolidated account summary, current equity, status and evaluation, challenge progress, drawdown and performance. These are the same values shown on the dashboard.\n\nmeta.stale is true when the upstream validator was unavailable and values fell back to placeholders. Retry when you see it.\n\nchallenge.drawdownCriteria is \"trailing\" (legacy HWM rules) or \"static\" (Rule 1 measured against the starting account balance, Rule 2 against the start-of-day balance, or the start-of-day equity on a Hyperliquid-linked account). For static accounts, challenge.drawdownBreakdown.highWaterMark carries the starting balance and the breakdown values reflect Rule 1, the 5% Static Loss Limit (equity vs the starting balance).\n\nproTrack, drawdownRules, elimination, isPro and tradingDays are additive fields (v1.2.0). Every field that existed before is unchanged.\n\ndrawdownRules describes both loss limits by rule. drawdownRules.criteria is the rule set the account is on: \"trailing\", \"static\" or \"pro\". dailyLoss is always the daily loss limit: equity against the start-of-day value in dayStartEquity, which is the start-of-day balance on standard accounts (evaluation, funded and the transition phase) and the start-of-day equity on pro, Instant Funded and Hyperliquid-linked accounts. maxLoss is the account's other limit, named by maxLoss.rule: \"eod_trailing\" (trailing and pro accounts: end-of-day equity against the end-of-day high-water mark, checked once a day at 00:00 UTC, so an intraday dip that recovers before then does not breach it) or \"static\" (static accounts: live equity against the starting balance). anchorEquity is what the limit is measured against and measuredEquity is the equity it is compared with, both in USD. usedPct and limitPct are percents. currentBalance is the balance in USD. drawdownRules is null when the validator sent no drawdown data.\n\nelimination is null unless the account is eliminated. elimination.reason is the network's reason code (or null when none was recorded), elimination.atMs is when the network eliminated the account (Unix ms, or null when unknown), elimination.drawdownPct is the drawdown at elimination as a percent (5.2 means 5.2%, or null when none was recorded) and forInactivity is true for the no-trade inactivity rule.\n\nproTrack and tradingDays are null for accounts that are not on the pro track. tradingDays is the number of full trading days the network has tracked.\n\nproTrack.phase is \"transition\" (the standard account winding down until the network starts the pro account: by transitionEndsAtMs, the next Monday 00:00 UTC in Unix ms, at the latest for an offered promotion; an automatic promotion starts as soon as the account is flat, or 7 days after it), \"challenge\" or \"funded\". transitionEndsAtMs is null outside the transition phase. standardAccountSize and proAccountSize are USD, or null when not reported. hasStats is false when the network sent no pro statistics on this read. evaluated turns true once the network has tracked the first full trading day.\n\nproTrack.objectives lists each promotion objective with key, label, value, threshold, higherIsBetter and met. The \"return\" value is the account return as a ratio (0.06 means 6%), \"calmar\" is the Calmar ratio, \"consistency\" is the best day's share of the total return as a ratio (0.2 means 20%; lower is better) and \"days\" is a count of full trading days. met is null while either the value or the threshold is unknown. The calmar and consistency values (and so their met) are also null until evaluated is true, because the network seeds them with placeholders before the first full trading day; return and days report met from day one.\n\nproTrack.maxDrawdownPercent is the all-time maximum drawdown as a percent (2.4 means 2.4%), unlike the objective ratios. softBreachApplies is true only for funded pro accounts, where a week with calmar or consistency below target has its reward held. softBreach is true while that is happening this week.","tags":["Trading"],"x-required-scope":"trade:read","parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"accountId":{"type":"string"},"assetClass":{"type":"string"},"marketName":{"type":"string"},"accountSize":{"type":"number"},"formattedAccountSize":{"type":"string"},"evaluation":{"type":"object","properties":{"title":{"type":"string"},"status":{"type":"string"},"isEliminated":{"type":"boolean"},"accountSize":{"type":"string"},"activeAccounts":{"type":"string"},"effectiveAccountSizeNumeric":{"type":"number"}}},"account":{"type":"object","properties":{"currentBalance":{"type":"number"},"currentEquity":{"type":"number"},"balanceChange":{"type":"number"},"balanceChangePercent":{"type":"number"},"totalPnL":{"type":"number"},"totalPnLPercent":{"type":"number"},"openPnL":{"type":"number"},"openPnLPercent":{"type":"number"},"openPositions":{"type":"number"},"portfolioBalance":{"type":"number"},"portfolioBalanceChangePercent":{"type":"number"},"portfolioBalanceBreakdown":{"type":"object","properties":{"currentBalance":{"type":"number"},"marginLeverage":{"type":"number"},"sumPositionValue":{"type":"number"}}},"leverage":{"type":"string"},"capitalUsed":{"type":"number"},"totalRealizedPnl":{"type":"number"},"isPassed":{"type":"boolean"}}},"challenge":{"type":"object","properties":{"variant":{"type":"string"},"bucket":{"type":"string"},"drawdownCriteria":{"type":"string"},"profitTarget":{"type":"number"},"profitTargetPercent":{"type":"number"},"remaining":{"type":"number"},"maxLeverage":{"type":"string"},"trailingDrawdownPercent":{"type":"number"},"maxDrawdown":{"type":"number"},"daysRemaining":{"type":"number"},"totalDays":{"type":"number"},"drawdownBreakdown":{"type":"object","properties":{"highWaterMark":{"type":"number"},"allowedDrawdown":{"type":"number"},"currentDrawdown":{"type":"number"},"remainingDrawdown":{"type":"number"},"remainingDrawdownPercentHWM":{"type":"number"}}}}},"performance":{"type":"object","properties":{"totalTrades":{"type":"number"},"winRate":{"type":"number"},"totalWins":{"type":"number"},"avgTradePnL":{"type":"number"},"tradeDuration":{"type":"string"},"challengeStartMs":{"type":"number"},"dailyReturns":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"value":{"type":"number"}}}}}},"meta":{"type":"object","properties":{"generatedAtMs":{"type":"number"},"stale":{"type":"boolean"}}},"proTrack":{"type":["object","null"],"properties":{"phase":{"type":"string"},"hasStats":{"type":"boolean"},"evaluated":{"type":"boolean"},"objectives":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"},"threshold":{"type":"number"},"higherIsBetter":{"type":"boolean"},"met":{"type":"boolean"}}}},"maxDrawdownPercent":{"type":"number"},"softBreachApplies":{"type":"boolean"},"softBreach":{"type":"boolean"},"transitionEndsAtMs":{"type":["number","null"]},"standardAccountSize":{"type":"number"},"proAccountSize":{"type":"number"}}},"drawdownRules":{"type":"object","properties":{"criteria":{"type":"string"},"dailyLoss":{"type":"object","properties":{"usedPct":{"type":"number"},"limitPct":{"type":"number"},"dayStartEquity":{"type":"number"}}},"maxLoss":{"type":"object","properties":{"rule":{"type":"string"},"usedPct":{"type":"number"},"limitPct":{"type":"number"},"anchorEquity":{"type":"number"},"measuredEquity":{"type":"number"}}},"currentBalance":{"type":"number"}}},"elimination":{"type":["object","null"],"properties":{"reason":{"type":"string"},"atMs":{"type":"number"},"drawdownPct":{"type":"number"},"forInactivity":{"type":"boolean"}}},"isPro":{"type":"boolean"},"tradingDays":{"type":["number","null"]}}}}},"example":{"success":true,"data":{"accountId":"6f1c2e34-9a4b-4c1d-8e2f-1a2b3c4d5e6f","assetClass":"crypto","marketName":"Crypto","accountSize":25000,"formattedAccountSize":"25K","evaluation":{"title":"Crypto 25K Classic","status":"evaluation","isEliminated":false,"accountSize":"$25,000","activeAccounts":"1/1","effectiveAccountSizeNumeric":25000},"account":{"currentBalance":25120.5,"currentEquity":25180.25,"balanceChange":120.5,"balanceChangePercent":0.48,"totalPnL":180.25,"totalPnLPercent":0.72,"openPnL":59.75,"openPnLPercent":0.24,"openPositions":1,"portfolioBalance":25120.5,"portfolioBalanceChangePercent":0.48,"portfolioBalanceBreakdown":{"currentBalance":25120.5,"marginLeverage":5,"sumPositionValue":7202.5},"leverage":"Current - 0.2870x / Max - 10x","capitalUsed":7202.5,"totalRealizedPnl":120.5,"isPassed":false},"challenge":{"variant":"default","bucket":"SUBACCOUNT_CHALLENGE","drawdownCriteria":"trailing","profitTarget":2500,"profitTargetPercent":10,"remaining":2319.75,"maxLeverage":"10x","trailingDrawdownPercent":5,"maxDrawdown":-1250,"daysRemaining":27,"totalDays":90,"drawdownBreakdown":{"highWaterMark":25180.25,"allowedDrawdown":1259.01,"currentDrawdown":0,"remainingDrawdown":1259.01,"remainingDrawdownPercentHWM":5}},"performance":{"totalTrades":4,"winRate":75,"totalWins":3,"avgTradePnL":45.06,"tradeDuration":"63h 12m","challengeStartMs":1717200000000,"dailyReturns":[{"date":"2026-06-28","value":0.31},{"date":"2026-06-29","value":0.17}]},"meta":{"generatedAtMs":1717718400000,"stale":false},"proTrack":null,"drawdownRules":{"criteria":"trailing","dailyLoss":{"usedPct":0.16,"limitPct":5,"dayStartEquity":25221.1},"maxLoss":{"rule":"eod_trailing","usedPct":0.16,"limitPct":5,"anchorEquity":25261.4,"measuredEquity":25221.1},"currentBalance":25120.5},"elimination":null,"isPro":false,"tradingDays":null}}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"},"502":{"$ref":"#/components/responses/Error502"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/v1/trading/positions":{"get":{"operationId":"getPositions","summary":"Open positions","description":"Open positions in the current challenge bucket, including entry price, leverage, unrealized PnL and any attached TP/SL.\n\nnetQuantity is the position size in the pair's lot unit, the same unit an order's quantity is submitted in: base units for crypto, equities, indices and Hyperliquid commodities; lots of 100,000 units for forex; 100 oz for XAU/USD; 5,000 oz for XAG/USD. It is signed like netLeverage, so a SHORT reports a negative number.\n\nnetQuantity is null only when the validator did not publish it and it cannot be derived from the position's fills. Treat null as unknown, never as zero.\n\nunrealizedPnl is the validator's live unrealized PnL in USD.","tags":["Trading"],"x-required-scope":"trade:read","parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"positionUuid":{"type":"string"},"tradePair":{"type":"string"},"tradePairDisplay":{"type":"string"},"positionType":{"type":"string"},"netLeverage":{"type":"number"},"netQuantity":{"type":"number"},"averageEntryPrice":{"type":"number"},"currentReturn":{"type":"number"},"openMs":{"type":"number"},"unrealizedPnl":{"type":"number"},"realizedPnl":{"type":"number"},"netValue":{"type":"number"},"cumulativeEntryValue":{"type":"number"},"stopLoss":{"type":"number"},"takeProfit":{"type":"number"}}}}}},"example":{"success":true,"data":[{"positionUuid":"0f9d1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b","tradePair":"BTCUSDC","tradePairDisplay":"BTC/USDC","positionType":"LONG","netLeverage":0.287,"netQuantity":0.11533,"averageEntryPrice":62450.12,"currentReturn":0.0024,"openMs":1717490000000,"unrealizedPnl":59.75,"realizedPnl":0,"netValue":7262.25,"cumulativeEntryValue":7202.5,"stopLoss":61000,"takeProfit":65000}]}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"},"502":{"$ref":"#/components/responses/Error502"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/v1/trading/orders":{"get":{"operationId":"getOrders","summary":"Pending orders","description":"Unfilled limit orders and per-position bracket legs (TP/SL) that will execute automatically.","tags":["Trading"],"x-required-scope":"trade:read","parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"orderUuid":{"type":"string"},"tradePair":{"type":"string"},"tradePairDisplay":{"type":"string"},"orderType":{"type":"string"},"executionType":{"type":"string"},"processedMs":{"type":"number"},"limitPrice":{"type":"number"},"leverage":{"type":"number"},"value":{"type":"number"},"quantity":{"description":"Nullable; null in the reference example."},"stopLoss":{"type":"number"},"takeProfit":{"type":"number"},"bracketPct":{"description":"Nullable; null in the reference example."},"trailingPercent":{"description":"Nullable; null in the reference example."},"trailingValue":{"description":"Nullable; null in the reference example."}}}}}},"example":{"success":true,"data":[{"orderUuid":"a12b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d","tradePair":"ETHUSDC","tradePairDisplay":"ETH/USDC","orderType":"LONG","executionType":"LIMIT","processedMs":1717500000000,"limitPrice":3200,"leverage":1,"value":1500,"quantity":null,"stopLoss":3100,"takeProfit":3500,"bracketPct":null,"trailingPercent":null,"trailingValue":null}]}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"},"502":{"$ref":"#/components/responses/Error502"},"503":{"$ref":"#/components/responses/Error503"}}},"post":{"operationId":"postOrders","summary":"Place an order","description":"Submit a market, limit or bracket order for the key's bound account. Also used to edit/cancel limit orders and flatten positions.\n\nexecution_type accepts MARKET, LIMIT, BRACKET, LIMIT_CANCEL, LIMIT_EDIT and FLAT_ALL.\n\ndata.order is the validator's order record parsed into typed fields. data.order_json is the same record as the raw string the validator returned and is kept for existing clients; prefer data.order.\n\nFor a MARKET order, order.filled is true and order.quantity, order.value and order.price are the executed size and fill price, after any cap clamp and lot-grid rounding. Compare them with what you sent. When the network filled less than you asked for, order.bindingCap names the limit that applied.\n\nFor LIMIT, STOP_LIMIT and BRACKET orders the record is the accepted resting order: order.filled is false and only the size field you sent is populated. Read the fill from netQuantity on GET /api/v1/trading/positions or quantity on GET /api/v1/trading/trades once it executes.\n\norder.quantity uses the same lot unit and sign convention as netQuantity on GET /api/v1/trading/positions.\n\norder.bracketOrders lists the TP/SL legs the network attached from stop_loss, take_profit or bracket_orders on the request, or null when there are none. Each leg carries orderUuid (what you would pass to LIMIT_CANCEL), stopLoss, takeProfit, quantity, value, bracketPct, trailingPercent and trailingValue, unused ones null.\n\nLIMIT_EDIT returns the edited resting order (order.filled is false).\n\nLIMIT_CANCEL and FLAT_ALL return data.order as null: a cancel has no order record, and FLAT_ALL closes several positions in one request. The cancelled id is data.order_uuid; read FLAT_ALL fills from GET /api/v1/trading/trades.\n\nMutating endpoints cannot be run from the browser. Copy the signed script and run it from your terminal.","tags":["Trading"],"x-required-scope":"trade:place","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string"},"trade":{"type":"object","properties":{"execution_type":{"type":"string"},"trade_pair":{"type":"string"},"order_type":{"type":"string"},"value":{"type":"number"}}}}},"example":{"accountId":"6f1c2e34-9a4b-4c1d-8e2f-1a2b3c4d5e6f","trade":{"execution_type":"MARKET","trade_pair":"BTCUSDC","order_type":"LONG","value":1000}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"success":{"type":"boolean"},"order_uuid":{"type":"string"},"message":{"type":"string"},"error_message":{"type":"string"},"processing_time":{"type":"number"},"order_json":{"type":"string"},"order":{"type":"object","properties":{"orderUuid":{"type":"string"},"tradePair":{"type":"string"},"tradePairDisplay":{"type":"string"},"orderType":{"type":"string"},"executionType":{"type":"string"},"filled":{"type":"boolean"},"quantity":{"type":"number"},"value":{"type":"number"},"leverage":{"type":"number"},"price":{"type":"number"},"slippage":{"type":"number"},"limitPrice":{"description":"Nullable; null in the reference example."},"stopPrice":{"description":"Nullable; null in the reference example."},"stopCondition":{"description":"Nullable; null in the reference example."},"stopLoss":{"description":"Nullable; null in the reference example."},"takeProfit":{"description":"Nullable; null in the reference example."},"bracketPct":{"description":"Nullable; null in the reference example."},"processedMs":{"type":"number"},"realizedPnl":{"type":"number"},"bindingCap":{"description":"Nullable; null in the reference example."},"bracketOrders":{"description":"Nullable; null in the reference example."}}}}}}},"example":{"success":true,"data":{"success":true,"order_uuid":"d45e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","message":"Order successfully processed by Taoshi validator","error_message":"","processing_time":1.23,"order_json":"{'trade_pair_id': 'BTCUSDC', 'trade_pair': ['BTCUSDC', 'BTC/USDC'], 'order_type': 'LONG', 'leverage': 0.04, 'value': 1000.0, 'quantity': 0.01601, 'price': 62450.12, ...}","order":{"orderUuid":"d45e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","tradePair":"BTCUSDC","tradePairDisplay":"BTC/USDC","orderType":"LONG","executionType":"MARKET","filled":true,"quantity":0.01601,"value":1000,"leverage":0.04,"price":62450.12,"slippage":0.0001,"limitPrice":null,"stopPrice":null,"stopCondition":null,"stopLoss":null,"takeProfit":null,"bracketPct":null,"processedMs":1717718400000,"realizedPnl":0,"bindingCap":null,"bracketOrders":null}}}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"}}}},"/api/v1/trading/trades":{"get":{"operationId":"getTrades","summary":"Trade history","description":"Closed/filled trades with entry & close price, realized PnL, return at close and fees.\n\nquantity is the total size opened over the life of the trade (every fill in the trade's direction, adds included), in the same lot unit and with the same sign convention as netQuantity on GET /api/v1/trading/positions. It is null when a fill predates quantity tracking on the network. positionSize is a display string and may be an estimate; reconcile against quantity.","tags":["Trading"],"x-required-scope":"trade:read","parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"tradePair":{"type":"string"},"tradePairDisplay":{"type":"string"},"positionType":{"type":"string"},"leverage":{"type":"string"},"positionSize":{"type":"string"},"quantity":{"type":"number"},"entryPrice":{"type":"string"},"closePrice":{"type":"string"},"unrealizedPnl":{"type":"number"},"realizedPnl":{"type":"number"},"returnAtClose":{"type":"number"},"status":{"type":"string"},"openTimeMs":{"type":"number"},"closeTimeMs":{"type":"number"},"stopLoss":{"type":"number"},"takeProfit":{"type":"number"},"totalFees":{"type":"number"}}}}}},"example":{"success":true,"data":[{"id":"c34d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f","tradePair":"BTCUSDC","tradePairDisplay":"BTC/USDC","positionType":"LONG","leverage":"0.50x","positionSize":"$1,000.00","quantity":0.01663,"entryPrice":"$60,120.00","closePrice":"$61,540.00","unrealizedPnl":0,"realizedPnl":23.62,"returnAtClose":0.0236,"status":"Filled","openTimeMs":1717200000000,"closeTimeMs":1717230000000,"stopLoss":59000,"takeProfit":62000,"totalFees":1.18}]}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"},"502":{"$ref":"#/components/responses/Error502"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/v1/trading/payouts":{"get":{"operationId":"getPayouts","summary":"Payouts","description":"Payout history for the account, newest first, plus the live estimate for the cycle in progress. items are the same payout records behind the dashboard's rewards history: amount is your share actually paid or payable after the profit split — the same figure as the Rewards page's Reward column — and weeks with no payout record (the page's $0 or held weeks) are not listed.\n\nThe query string is part of the signed path: sign /api/v1/trading/payouts?limit=100&cursor=... exactly as you send it.\n\nAmounts are USD. amount is the trader share paid (or payable) for the row after the profit split, including any earlier weeks that were rolled into it. profitSplit is the split applied, as a percent label such as \"100%\" or \"200%\".\n\nitems covers closed cycles only. The cycle in progress is not a row; it appears as accrual.\n\naccrualStatus is \"ok\" when accrual carries a live estimate, \"not_applicable\" when the account does not earn payouts in its current phase or is not yet set up on the network, \"unavailable\" when the validator could not be reached (items is still returned; retry for the estimate) and \"omitted\" on every page after the first.\n\naccrual.estimatedAmount is null while the current window is still empty or while the network has no payout data for the account yet (for example an account that has not traded since it entered its current phase). priorUnpaidAmount is the total of closed cycles that have not been paid yet.\n\nPaginate by passing nextCursor back as cursor until nextCursor is null. The accrual is computed on the first page only.","tags":["Trading"],"x-required-scope":"trade:read","parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Optional. Rows per page, 1 to 200 (default 50). Values above 200 are treated as 200; anything that is not a positive integer is rejected with 400.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"Optional. The nextCursor value from the previous page, passed back unchanged. Omit it for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"periodStartMs":{"type":"number"},"periodEndMs":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"},"profitSplit":{"type":"string"},"status":{"type":"string"},"rail":{"type":"string"},"provider":{"type":"string"},"alphaAmount":{"description":"Nullable; null in the reference example."},"alphaDestination":{"description":"Nullable; null in the reference example."},"requestedAtMs":{"type":"number"},"paidAtMs":{"type":"number"},"createdAtMs":{"type":"number"}}}},"nextCursor":{"description":"Nullable; null in the reference example."},"accrualStatus":{"type":"string"},"accrual":{"type":"object","properties":{"estimatedAmount":{"type":"number"},"windowStartMs":{"type":"number"},"asOfMs":{"type":"number"},"priorUnpaidAmount":{"type":"number"},"earliestUnpaidPeriodStartMs":{"description":"Nullable; null in the reference example."},"deferredBalance":{"description":"Nullable; null in the reference example."}}}}}}},"example":{"success":true,"data":{"items":[{"id":"e56f7a8b-9c0d-4e1f-8a3b-4c5d6e7f8a9b","periodStartMs":1757894400000,"periodEndMs":1758499200000,"amount":412.5,"currency":"USD","profitSplit":"100%","status":"completed","rail":"crypto","provider":"rise","alphaAmount":null,"alphaDestination":null,"requestedAtMs":1758502800000,"paidAtMs":1758585600000,"createdAtMs":1758499260000}],"nextCursor":null,"accrualStatus":"ok","accrual":{"estimatedAmount":88.25,"windowStartMs":1758499200000,"asOfMs":1758800000000,"priorUnpaidAmount":0,"earliestUnpaidPeriodStartMs":null,"deferredBalance":null}}}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"},"502":{"$ref":"#/components/responses/Error502"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/v1/trading/trade-pairs":{"get":{"operationId":"getTradePairs","summary":"Trade pairs","description":"The pairs the key's account may trade: the network's allow-list for the account's asset class (all_markets, crypto, forex, equities, indices or commodities), narrowed to the pro universe for a pro account, with each pair's category, source and leverage bounds. Pairs the network lists for the class but no longer allows come back under disabled.\n\ntradePairId is the value POST /api/v1/trading/orders takes as trade.trade_pair (BTCUSDC, EURUSD, NVDA); tradePair is the display spelling (BTC/USDC, EUR/USD).\n\nassetClass is the account's class as stored; tradePairsAssetClass is the network class the list was read for (a legacy hl_all account reads all_markets, fx reads forex).\n\nisPro says whether the list is the pro universe. A pro account is answered only the pairs it may trade; a standard account is answered the class's full allow-list, which includes pairs flagged isPro: false that a pro account could not trade.\n\ncategory is the network's own filing (crypto, forex, equities, indices, commodities) and source says which book the pair trades on: hyperliquid for the Hyperliquid-built perps (quoted in USDC) and vanta for Vanta's cash pairs.\n\nlotSize is set for the commodities that trade in lots and null otherwise; hlCoin is the Hyperliquid asset id behind the pair when it has one.\n\nThe list is served from a process cache of the network's catalog: normally under a minute old, and up to ten minutes old while a refresh of it runs, when meta.stale is true. A pair the network delists can appear for that long; an order for it is rejected by the network either way.\n\nA 503 means the network's catalog could not be read, or came back without an allow-list; retry shortly. The endpoint never answers an empty allow-list in place of an outage.","tags":["Trading"],"x-required-scope":"trade:read","parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"accountId":{"type":"string"},"assetClass":{"type":"string"},"tradePairsAssetClass":{"type":"string"},"isPro":{"type":"boolean"},"readAtMs":{"type":"number"},"validatorTimestampMs":{"type":"number"},"allowed":{"type":"array","items":{"type":"object","properties":{"tradePairId":{"type":"string"},"tradePair":{"type":"string"},"category":{"type":"string"},"source":{"type":"string"},"isPro":{"type":"boolean"},"maxLeverage":{"type":"number"},"minLeverage":{"type":"number"},"lotSize":{"type":"number"},"hlCoin":{"type":"string"}}}},"disabled":{"type":"array","items":{"type":"object","properties":{"tradePairId":{"type":"string"},"tradePair":{"type":"string"},"category":{"type":"string"},"source":{"type":"string"},"isPro":{"type":"boolean"},"maxLeverage":{"type":"number"},"minLeverage":{"type":"number"},"lotSize":{"description":"Nullable; null in the reference example."},"hlCoin":{"type":"string"}}}},"totalAllowed":{"type":"number"},"totalDisabled":{"type":"number"},"meta":{"type":"object","properties":{"generatedAtMs":{"type":"number"},"stale":{"type":"boolean"}}}}}}},"example":{"success":true,"data":{"accountId":"6f1c2e34-9a4b-4c1d-8e2f-1a2b3c4d5e6f","assetClass":"all_markets","tradePairsAssetClass":"all_markets","isPro":false,"readAtMs":1791920800000,"validatorTimestampMs":1791920788376,"allowed":[{"tradePairId":"GOLDUSDC","tradePair":"GOLD/USDC","category":"commodities","source":"hyperliquid","isPro":true,"maxLeverage":1,"minLeverage":0.01,"lotSize":100,"hlCoin":"xyz:GOLD"},{"tradePairId":"BTCUSDC","tradePair":"BTC/USDC","category":"crypto","source":"hyperliquid","isPro":true,"maxLeverage":1,"minLeverage":0.01,"lotSize":null,"hlCoin":"BTC"},{"tradePairId":"EURUSD","tradePair":"EUR/USD","category":"forex","source":"vanta","isPro":true,"maxLeverage":10,"minLeverage":0.1,"lotSize":null,"hlCoin":"EUR"}],"disabled":[{"tradePairId":"XAUUSD","tradePair":"XAU/USD","category":"forex","source":"vanta","isPro":false,"maxLeverage":2,"minLeverage":0.05,"lotSize":null,"hlCoin":"XAU"}],"totalAllowed":3,"totalDisabled":1,"meta":{"generatedAtMs":1791920800000,"stale":false}}}}}},"400":{"$ref":"#/components/responses/Error400"},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"429":{"$ref":"#/components/responses/Error429"},"500":{"$ref":"#/components/responses/Error500"},"502":{"$ref":"#/components/responses/Error502"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/v1/keys/self":{"get":{"operationId":"getKeySelf","summary":"Key details","description":"The key that signed the request: its key id, the account it is bound to, the scopes it carries, and whether its order placement is paused by the failed-request cooldown. Call it to learn what a key may do before you call an endpoint that needs a scope.\n\nAny active key may call it, whatever scopes it carries, including a key without trade:read. It never answers 403 insufficient_scope.\n\nscopes lists the scopes the key was issued with, fixed when it was created. The GET /api/v1/trading/* reads need trade:read, and POST /api/v1/trading/orders needs trade:place for every execution type, closing and cancelling included. No endpoint requires trade:close today. Treat a value you do not recognise as one you cannot rely on.\n\nA scope says which endpoints the key may call, not that a given request will succeed: an order can still be refused for the account's own state.\n\norderPlacementPausedUntil is null unless the key's order placement is paused by the failed-request cooldown: a key that accumulates 30 rejected requests within 10 minutes is paused for 15, and until then POST /api/v1/trading/orders answers 429 too_many_failed_requests. While it is paused, the field is the ISO 8601 time (UTC) at which the cooldown ends, the same moment that route's Retry-After counts down to. It says nothing about rejections short of the threshold, and nothing about the reads, this endpoint included, which are never paused. The field is additive: a client that ignores it loses nothing, and null is its value whenever nothing is paused.\n\nA revoked or deleted key is refused with 401 Invalid key before this endpoint answers, so every 200 describes a key that is in use. Keys do not expire, so there is no expiry to report.\n\nIt answers only for the key that signed the request; there is no way to ask about another key. The secret is never returned: it is shown once, when the key is created.\n\nIt describes the key, not the account. The account's state is GET /api/v1/trading/account, which needs trade:read. A key whose account is no longer its owner's gets 404 Account not found here, as it does from every other endpoint.\n\nIt counts against the same per-key read budget as the trading reads and answers 429 rate_limited the same way once that budget is spent.","tags":["Keys"],"x-required-scope":null,"parameters":[{"name":"accountId","in":"query","required":false,"description":"Optional. The key is already bound to one account; when supplied this must match it, or the request is rejected with 403.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"keyId":{"type":"string"},"accountId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"orderPlacementPausedUntil":{"type":["string","null"]}}}}},"example":{"success":true,"data":{"keyId":"vk_EXAMPLEKEYID000000","accountId":"6f1c2e34-9a4b-4c1d-8e2f-1a2b3c4d5e6f","scopes":["trade:place","trade:close","trade:read"],"orderPlacementPausedUntil":null}}}}},"401":{"description":"One or more of the four auth headers is absent or empty. Note that all four are required on every request, including GETs. / X-Vanta-Timestamp is not a positive finite number. It is most often seconds since the epoch, an ISO date string, or a float. / The timestamp is more than 5 minutes away from server time in either direction. Usually a clock drift problem on the caller's machine, or a request that sat in a queue before being sent. / No active key matches X-Vanta-Key-Id: the key id is wrong, or the key has been revoked or deleted. / The HMAC did not match. The credential is recognised, so the fault is in the canonical string, the body hash, or the secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing-credentials":{"summary":"One or more of the four auth headers is absent or empty. Note that all four are required on every request, including GETs.","value":{"error":"Missing credentials"}},"invalid-timestamp":{"summary":"X-Vanta-Timestamp is not a positive finite number. It is most often seconds since the epoch, an ISO date string, or a float.","value":{"error":"Invalid timestamp"}},"timestamp-skew":{"summary":"The timestamp is more than 5 minutes away from server time in either direction. Usually a clock drift problem on the caller's machine, or a request that sat in a queue before being sent.","value":{"error":"Timestamp invalid"}},"invalid-key":{"summary":"No active key matches X-Vanta-Key-Id: the key id is wrong, or the key has been revoked or deleted.","value":{"error":"Invalid key"}},"bad-signature":{"summary":"The HMAC did not match. The credential is recognised, so the fault is in the canonical string, the body hash, or the secret.","value":{"error":"Bad signature"}}}}}},"403":{"description":"Each key is bound to exactly one prop account. Either the ?accountId= query parameter or the accountId in the request body names a different one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"wrong-account":{"summary":"Each key is bound to exactly one prop account. Either the ?accountId= query parameter or the accountId in the request body names a different one.","value":{"error":"This API key is only valid for a different account"}}}}}},"404":{"description":"The bound account no longer resolves for the key's owner. It was deleted, transferred, or never fully provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"account-not-found":{"summary":"The bound account no longer resolves for the key's owner. It was deleted, transferred, or never fully provisioned.","value":{"error":"Account not found"}}}}}},"409":{"description":"This nonce has already been used by this key. Nonces are stored permanently and are single use for the life of the key. This is the replay protection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"nonce-replay":{"summary":"This nonce has already been used by this key. Nonces are stored permanently and are single use for the life of the key. This is the replay protection.","value":{"error":"Nonce"}}}}}},"429":{"description":"The per-key read budget for the current minute is exhausted. The response carries Retry-After plus X-RateLimit-Limit / -Remaining / -Reset.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests permitted per window. Present on read-budget 429s only.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window. Present on read-budget 429s only.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix seconds at which the window resets. Present on read-budget 429s only.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate-limited":{"summary":"The per-key read budget for the current minute is exhausted. The response carries Retry-After plus X-RateLimit-Limit / -Remaining / -Reset.","value":{"error":"rate_limited"}}}}}},"500":{"description":"The stored key secret could not be decrypted server-side. This is a platform fault, not a problem with your request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"decrypt-failed":{"summary":"The stored key secret could not be decrypted server-side. This is a platform fault, not a problem with your request.","value":{"error":"Decrypt failed"}}}}}}}}}},"components":{"securitySchemes":{"VantaKeyId":{"type":"apiKey","in":"header","name":"X-Vanta-Key-Id","description":"The key id shown when the key was created (`vk_…`)."},"VantaTimestamp":{"type":"apiKey","in":"header","name":"X-Vanta-Timestamp","description":"Whole milliseconds since the Unix epoch, as a decimal string. Must be within 5 minutes of server time."},"VantaNonce":{"type":"apiKey","in":"header","name":"X-Vanta-Nonce","description":"Unique random value per request. Single-use for the life of the key; replays are rejected with 409."},"VantaSignature":{"type":"apiKey","in":"header","name":"X-Vanta-Signature","description":"`v1=` followed by the base64 HMAC-SHA256 of the canonical string."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable failure reason."},"required_scope":{"type":"string","description":"Present only on 403 insufficient_scope responses."}},"required":["error"],"example":{"error":"This API key is only valid for a different account"}}},"responses":{"Error400":{"description":"The order body failed validation. The message names the first problem found, such as an unknown execution_type, a malformed accountId, or a wrong field type. / A query parameter failed validation. GET /api/v1/trading/payouts answers \"limit must be an integer between 1 and 200\" for a limit that is not a positive integer, and \"invalid cursor\" for a cursor that was not returned by the API. / Exactly one sizing field is required on MARKET and LIMIT orders. Sending none, or sending two, is rejected. / The account exists but has not finished provisioning on the trading network, so no orders can be routed to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid-request":{"summary":"The order body failed validation. The message names the first problem found, such as an unknown execution_type, a malformed accountId, or a wrong field type.","value":{"error":"<field-specific validation message>"}},"invalid-query":{"summary":"A query parameter failed validation. GET /api/v1/trading/payouts answers \"limit must be an integer between 1 and 200\" for a limit that is not a positive integer, and \"invalid cursor\" for a cursor that was not returned by the API.","value":{"error":"<query-specific validation message>"}},"missing-size":{"summary":"Exactly one sizing field is required on MARKET and LIMIT orders. Sending none, or sending two, is rejected.","value":{"error":"'leverage', 'value', or 'quantity' must be provided"}},"account-not-ready":{"summary":"The account exists but has not finished provisioning on the trading network, so no orders can be routed to it.","value":{"error":"Account is not ready for trading (missing subaccount_id)"}}}}}},"Error401":{"description":"One or more of the four auth headers is absent or empty. Note that all four are required on every request, including GETs. / X-Vanta-Timestamp is not a positive finite number. It is most often seconds since the epoch, an ISO date string, or a float. / The timestamp is more than 5 minutes away from server time in either direction. Usually a clock drift problem on the caller's machine, or a request that sat in a queue before being sent. / No active key matches X-Vanta-Key-Id: the key id is wrong, or the key has been revoked or deleted. / The HMAC did not match. The credential is recognised, so the fault is in the canonical string, the body hash, or the secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing-credentials":{"summary":"One or more of the four auth headers is absent or empty. Note that all four are required on every request, including GETs.","value":{"error":"Missing credentials"}},"invalid-timestamp":{"summary":"X-Vanta-Timestamp is not a positive finite number. It is most often seconds since the epoch, an ISO date string, or a float.","value":{"error":"Invalid timestamp"}},"timestamp-skew":{"summary":"The timestamp is more than 5 minutes away from server time in either direction. Usually a clock drift problem on the caller's machine, or a request that sat in a queue before being sent.","value":{"error":"Timestamp invalid"}},"invalid-key":{"summary":"No active key matches X-Vanta-Key-Id: the key id is wrong, or the key has been revoked or deleted.","value":{"error":"Invalid key"}},"bad-signature":{"summary":"The HMAC did not match. The credential is recognised, so the fault is in the canonical string, the body hash, or the secret.","value":{"error":"Bad signature"}}}}}},"Error403":{"description":"The key does not carry the scope the endpoint requires. The response also includes a required_scope field naming it. / Each key is bound to exactly one prop account. Either the ?accountId= query parameter or the accountId in the request body names a different one. / The account's owner set personal limits on it in the trading terminal (a daily loss limit, a cap on new entries per day, or a maximum order size), and one of them forbids this order, which would open a position or add to one. The message names the limit, for example \"Locked until 00:00 UTC — your personal daily loss limit of $500 was reached. You can still close and reduce.\", \"You've reached your personal limit of 5 new entries today. It resets at 00:00 UTC. You can still close and reduce.\" or \"This order is larger than your personal maximum order size of $10,000.\". Only orders that add exposure are refused: closes, reductions, FLAT_ALL, LIMIT_CANCEL and BRACKET (TP/SL) orders pass, as does a LIMIT_EDIT of a position's TP/SL legs, with two exceptions. A reduction of a position opened or added to since the account's latest snapshot reads as an opener on that snapshot, so for up to 15 seconds after the snapshot is saved, while the network is slow or unreachable, it can be refused; a FLAT of that position always passes. And a close cancels only TP/SL orders, so a resting LIMIT can still fill after its position is gone: a LIMIT against the position counts as an opener when, with the other resting orders on its side, it is larger than the position. A LIMIT sized by value on a forex pair is not measured against the position and passes. An edit of a resting LIMIT (a LIMIT_EDIT with its order_uuid) that keeps its side and sends a quantity no larger than the one resting, such as a price change, passes; an edit that grows the order or turns it to the other side is judged like a new LIMIT in its place.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"insufficient-scope":{"summary":"The key does not carry the scope the endpoint requires. The response also includes a required_scope field naming it.","value":{"error":"insufficient_scope","required_scope":"trade:read"}},"wrong-account":{"summary":"Each key is bound to exactly one prop account. Either the ?accountId= query parameter or the accountId in the request body names a different one.","value":{"error":"This API key is only valid for a different account"}},"personal-limit":{"summary":"The account's owner set personal limits on it in the trading terminal (a daily loss limit, a cap on new entries per day, or a maximum order size), and one of them forbids this order, which would open a position or add to one. The message names the limit, for example \"Locked until 00:00 UTC — your personal daily loss limit of $500 was reached. You can still close and reduce.\", \"You've reached your personal limit of 5 new entries today. It resets at 00:00 UTC. You can still close and reduce.\" or \"This order is larger than your personal maximum order size of $10,000.\". Only orders that add exposure are refused: closes, reductions, FLAT_ALL, LIMIT_CANCEL and BRACKET (TP/SL) orders pass, as does a LIMIT_EDIT of a position's TP/SL legs, with two exceptions. A reduction of a position opened or added to since the account's latest snapshot reads as an opener on that snapshot, so for up to 15 seconds after the snapshot is saved, while the network is slow or unreachable, it can be refused; a FLAT of that position always passes. And a close cancels only TP/SL orders, so a resting LIMIT can still fill after its position is gone: a LIMIT against the position counts as an opener when, with the other resting orders on its side, it is larger than the position. A LIMIT sized by value on a forex pair is not measured against the position and passes. An edit of a resting LIMIT (a LIMIT_EDIT with its order_uuid) that keeps its side and sends a quantity no larger than the one resting, such as a price change, passes; an edit that grows the order or turns it to the other side is judged like a new LIMIT in its place.","value":{"error":"<personal limit message>"}}}}}},"Error404":{"description":"The bound account no longer resolves for the key's owner. It was deleted, transferred, or never fully provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"account-not-found":{"summary":"The bound account no longer resolves for the key's owner. It was deleted, transferred, or never fully provisioned.","value":{"error":"Account not found"}}}}}},"Error409":{"description":"This nonce has already been used by this key. Nonces are stored permanently and are single use for the life of the key. This is the replay protection.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"nonce-replay":{"summary":"This nonce has already been used by this key. Nonces are stored permanently and are single use for the life of the key. This is the replay protection.","value":{"error":"Nonce"}}}}}},"Error429":{"description":"The per-key read budget for the current minute is exhausted. The response carries Retry-After plus X-RateLimit-Limit / -Remaining / -Reset. / The key accumulated 30 rejected requests within 10 minutes, so order placement is paused for a 15-minute cooldown. Retrying the same failing request in a loop is what triggers this.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests permitted per window. Present on read-budget 429s only.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window. Present on read-budget 429s only.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix seconds at which the window resets. Present on read-budget 429s only.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate-limited":{"summary":"The per-key read budget for the current minute is exhausted. The response carries Retry-After plus X-RateLimit-Limit / -Remaining / -Reset.","value":{"error":"rate_limited"}},"too-many-failed-requests":{"summary":"The key accumulated 30 rejected requests within 10 minutes, so order placement is paused for a 15-minute cooldown. Retrying the same failing request in a loop is what triggers this.","value":{"error":"too_many_failed_requests"}}}}}},"Error500":{"description":"The stored key secret could not be decrypted server-side. This is a platform fault, not a problem with your request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"decrypt-failed":{"summary":"The stored key secret could not be decrypted server-side. This is a platform fault, not a problem with your request.","value":{"error":"Decrypt failed"}}}}}},"Error502":{"description":"The upstream trade-history service answered with an error rather than data.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"upstream-failure":{"summary":"The upstream trade-history service answered with an error rather than data.","value":{"error":"<upstream error message>"}}}}}},"Error503":{"description":"The upstream validator could not be reached. The endpoint fails closed on purpose: it will not return placeholder balances that a polling client would record as real account history. / GET /api/v1/trading/trade-pairs could not read the network's pair catalog, or the catalog came back without an allow-list. The endpoint fails closed: it never answers an empty list in place of an outage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"snapshot-unavailable":{"summary":"The upstream validator could not be reached. The endpoint fails closed on purpose: it will not return placeholder balances that a polling client would record as real account history.","value":{"error":"Account snapshot temporarily unavailable (PTN validator unreachable); retry shortly"}},"trade-pairs-unavailable":{"summary":"GET /api/v1/trading/trade-pairs could not read the network's pair catalog, or the catalog came back without an allow-list. The endpoint fails closed: it never answers an empty list in place of an outage.","value":{"error":"Trade pairs temporarily unavailable (validator unreachable); retry shortly"}}}}}}}}}