API Documentation
Overview
The Mongo Trader API gives you programmatic access to your portfolios. Query your positions, view trade history, analyze performance metrics, and execute trades—all through authenticated REST endpoints. Combine with any market data provider to build and run your own custom trading algorithms.
Authentication
All API requests require authentication via API key in the X-API-Key header. Generate an API key from your account settings.
X-API-Key: mt_your_api_key_herePermission Levels: API keys can be created with Read Only (view data only) or Full Access (view + execute trades) permissions.
Base URL
https://api.mongotrader.com/v1Agentic Onboarding
An autonomous agent can self-onboard end-to-end — no human in the loop — to obtain its own API key. These three endpoints live under /v1/auth and do not require an API key (the agent has none yet). They are rate-limited harder than the trading endpoints because onboarding is the abuse-sensitive surface.
POST /v1/auth/register— create a FREE account with an email the agent controls. A verification link is emailed there.POST /v1/auth/verify-email— submit the token from that link. Returns a short-lived bearer access token.POST /v1/auth/api-key— using that bearer token, mint one scoped API key (mt_...). Shown once.
/v1/auth/registerNo keyRegister a new FREE account programmatically. No tokens are returned — the email must be verified first. Disposable-email domains are rejected. Limit: 5 requests/hour per IP.
{
"email": "[email protected]",
"username": "my_agent", // 3-30 chars, [a-zA-Z0-9_]
"password": "Str0ngPass" // 8-128, upper+lower+digit
}{
"data": {
"userId": "...",
"username": "my_agent",
"email": "[email protected]",
"emailVerified": false,
"nextStep": "Check the inbox for a verification link, extract the token query param, then POST /v1/auth/verify-email."
}
}/v1/auth/verify-emailNo keyVerify the email with the token from the link. Returns a short-lived bearer access token used once to mint the key. Limit: 10 requests/15 min per IP.
{ "token": "<token from the verification link>" }{
"data": {
"verified": true,
"accessToken": "eyJ...", // bearer token
"expiresIn": 900,
"nextStep": "Send this as Authorization: Bearer <accessToken> to POST /v1/auth/api-key."
}
}/v1/auth/api-keyBearer tokenMint the account's API key. Authenticated with the bearer token from verify-email (Authorization: Bearer ...). FREE accounts may hold a single key. The plain key is returned once. Limit: 5 requests/hour per IP.
{
"name": "my-agent-key",
"permission": "FULL_ACCESS" // or READ_ONLY
}{
"data": {
"key": "mt_...", // shown ONLY once — save it
"apiKey": { "id": "...", "name": "my-agent-key", "permission": "FULL_ACCESS" },
"message": "Save this key now — send it as the X-API-Key header on all /v1 requests."
}
}FREE-tier key limits: 1 active key, MARKET orders only, no leverage, and a daily trade cap (resets 00:00 UTC). Upgrade to Premium/Max via the Billing endpoints below to unlock all order types, leverage, and up to 4 keys.
Symbology & Asset Types
Orders are routed by symbol string. Several bare crypto tickers collide with real ETFs and stocks, so MongoTrader classifies symbols registry-first and refuses to guess. Send the wrong form and your order will fill against a completely different asset.
These bare symbols are NOT cryptocurrencies on this API — they are equity tickers:
BTC→ Grayscale Bitcoin Trust ETF (~$27, NOT bitcoin)ETH→ Grayscale Ethereum Trust ETF (NOT ether)DIA→ SPDR Dow Jones Industrial Average ETF
To trade crypto, always use the pair form: BTCUSDT, ETHUSDT, SOLUSDT, etc. BTCUSD is also accepted and normalized to BTCUSDT internally.
Asset classes
The assetType field returned by the lookup and order endpoints uses these values:
STOCKCommon equityETFExchange-traded fundCRYPTOCrypto pairFOREXFX pairINDEXMarket indexFUTUREFutures contractFUNDMutual fundREITReal-estate trustADRDepositary receipt/symbols/:symbolRead OnlyLook up a symbol's asset class before placing an order. Use this whenever the symbol could be ambiguous — particularly any bare ticker you intend as crypto. Returns the canonical normalized form, the asset class from the registry, and a warning message when the input looks like a likely mis-identification.
GET /v1/symbols/BTC
{
"data": {
"input": "BTC",
"normalizedSymbol": "BTC",
"assetType": "ETF",
"isCrypto": false,
"message": "\"BTC\" is registered as a ETF, NOT a cryptocurrency. To trade the crypto instead, submit the pair form: BTCUSDT."
}
}GET /v1/symbols/BTCUSDT
{
"data": {
"input": "BTCUSDT",
"normalizedSymbol": "BTCUSDT",
"assetType": "CRYPTO",
"isCrypto": true
}
}GET /v1/symbols/AAPL
{
"data": {
"input": "AAPL",
"normalizedSymbol": "AAPL",
"assetType": "STOCK",
"isCrypto": false
}
}The POST /portfolios/:id/orders response also echoes assetType and normalizedSymbol so you can confirm after the order was placed what the engine actually traded.
Pre-Trade Context
Three read-only endpoints for reasoning about the next trade before placing it — quote freshness, session state, and capability headroom. All cache-backed; no extra provider load.
/quotes/:symbolRead OnlyCurrent cached quote for ONE symbol. Cache-first — served from the same in-memory map that powers live UI charts, so calling this adds zero provider load. Single-symbol and current-only by design (no batch endpoint, no historical OHLCV — both are licensing surfaces).
GET /v1/quotes/AAPL
{
"data": {
"symbol": "AAPL",
"assetType": "STOCK",
"last": 174.25,
"bid": 174.24,
"ask": 174.26,
"open": 173.50,
"high": 175.10,
"low": 173.20,
"previousClose": 173.84,
"change": 0.41,
"changePercent": 0.236,
"volume": 12345678,
"asOf": "2026-06-09T14:45:30.000Z",
"source": "websocket"
}
}source values: websocket = live tick (freshest), rest = REST snapshot (cold-cache fallback, may be a few seconds stale), database = derived from locally stored candles (crypto fallback path, can lag a minute), inactive = symbol not in our registry (typo, delisted, or untracked — verify with /symbols/:symbol).
/market-statusRead OnlyCurrent US equity session state + crypto status. Use to plan around session transitions — MARKET_ON_CLOSE only fires at 16:00 ET; a LIMIT placed during CLOSED sits until 09:30 next trading day.
GET /v1/market-status
{
"data": {
"stocks": {
"session": "REGULAR", // PRE | REGULAR | AFTER | CLOSED
"isOpen": true,
"isTradingDay": true,
"isHoliday": false,
"nextOpen": "2026-06-10T13:30:00.000Z",
"nextClose": "2026-06-09T20:00:00.000Z",
"timezone": "America/New_York"
},
"crypto": {
"session": "OPEN",
"isOpen": true,
"note": "Cryptocurrency markets run 24/7 — no session boundaries."
},
"now": "2026-06-09T17:30:00.000Z"
}
}Session windows follow NYSE/ARCA standard — PRE 04:00-09:30 ET, REGULAR 09:30-16:00, AFTER 16:00-20:00, CLOSED otherwise. nextOpen/nextClose skip weekends + NYSE holidays.
/limitsRead OnlyTier-bound capability envelope + current usage in one read. Per-dimension { max, current, remaining }; max: null denotes "unlimited" for the tier. Lets an agent decide what's feasible TODAY without learning by triggering a FREE_TIER_* error.
GET /v1/limits
{
"data": {
"tier": "FREE",
"portfolios": { "max": 3, "current": 2, "remaining": 1 },
"apiKeys": { "max": 1, "current": 1, "remaining": 0 },
"tradesPerDay": {
"max": 10, "current": 4, "remaining": 6,
"resetsAt": "2026-06-10T00:00:00.000Z"
},
"orderTypes": { "allowed": ["MARKET"] },
"leverage": { "max": 1 },
"shorts": { "allowed": false },
"activeStreams":{ "max": 1 }
}
}portfolios.current filters archived portfolios (matches what POST /v1/portfolios actually enforces). tradesPerDay.resetsAt is the next 00:00 UTC.
Trading Endpoints
Portfolios
/portfoliosRead OnlyList all portfolios for the authenticated user.
{
"data": [
{
"id": "portfolio_123",
"name": "My Portfolio",
"type": "PAPER",
"totalValue": 125000.00,
"cashBalance": 25000.00,
"positionsCount": 5,
"createdAt": "2024-01-01T00:00:00Z"
}
]
}/portfolios/:idRead OnlyGet detailed portfolio information including cash balance, margin state, buying power, and maintenance requirements. Use the margin fields to size leveraged orders correctly.
{
"data": {
"id": "portfolio_123",
"name": "My Portfolio",
"type": "PAPER",
"totalValue": 125000.00,
"cashBalance": 25000.00,
"positionsValue": 100000.00,
"totalGainLoss": 5000.00,
"totalGainLossPercent": 4.17,
// Portfolio day P&L on the positions' own bases: sum of each open
// position's dayChange + day-basis P&L of units closed today - today's fees.
"dayChange": 312.40,
"dayChangePercent": 0.25,
"dayChangeSource": "positions", // "positions" | "snapshot" (fallback: value vs first snapshot after open)
"positionsDayChange": 290.10, // sum of open positions' dayChange
"closedDayChange": 25.30, // day-basis P&L of units closed today
"realizedToday": 180.00, // cost-basis P&L locked in by today's closing fills
"feesToday": 3.00,
"closedToday": [ // symbols closed to flat today (absent from /positions)
{
"symbol": "TSLA",
"quantity": 10,
"isShort": false,
"averageCost": 240.00,
"averageExit": 258.00,
"realizedPL": 180.00,
"realizedPLPercent": 7.5,
"dayChange": 25.30
}
],
// Margin / leverage fields
"equity": 125000.00, // cash + longMV - shortMV - marginLoan
"buyingPower": 12500000.00, // how much notional this user can open next
"grossExposure": 200000.00, // |long| + |short| market value
"maintenanceRequirement": 60000.00, // sum of per-position tiered MMR (30% at L≤2 → 0.5% at L>20)
"marginLoan": 75000.00, // implicit borrowed amount on leveraged longs
"marginHealth": "HEALTHY", // HEALTHY | WARNING | CALL
"hasLeverageOrShort": true, // true if any position is leveraged or short
"tags": ["growth", "tech"]
}
}/portfoliosFull AccessCreate a new PAPER portfolio. Capped at your subscription tier's portfolio limit (Free 3, Premium 20, Max unlimited) — exceeding it returns PORTFOLIO_LIMIT_REACHED.
{
"name": "My Bot Portfolio",
"balance": 100000, // optional starting paper cash (default applies if omitted)
"tags": ["bot", "v1"] // optional, up to 5 tags
}Positions
/portfolios/:id/positionsRead OnlyList all positions in a portfolio with current market values, P&L, and leverage metadata.
{
"data": [
{
"symbol": "AAPL",
"quantity": 100, // negative values indicate short positions
"averageCost": 150.00,
"currentPrice": 175.50,
"marketValue": 17550.00,
"unrealizedPL": 2550.00,
"unrealizedPLPercent": 17.00,
// Position day P&L: lots held overnight vs previous close, lots opened
// today vs their fill price. Sign-correct for shorts.
"dayChange": 125.00,
"dayChangePercent": 0.72,
"dayBasis": "previousClose", // "previousClose" | "cost" (opened today) | "mixed"
"openedToday": false,
"realizedToday": 0, // cost-basis P&L on units of this symbol closed today
"openedQuantityToday": 0, // units opened inside today's window
"closedQuantityToday": 0, // units closed inside today's window
"priceChange": 1.25, // instrument's per-unit move vs previous close
"priceChangePercent": 0.72,
// Leverage fields — 1 / 0 / null for unleveraged positions
"effectiveLeverage": 10, // weighted avg leverage across fills
"marginPosted": 1500.00, // cash actually at risk on this position
"liquidationPrice": 105.80 // estimate at this position's tiered MMR
}
]
}Trades
/portfolios/:id/tradesRead OnlyGet trade history with optional date range and symbol filtering.
fromISO date stringtoISO date stringsymbolFilter by symbollimit1-100 (default 100)offsetSkip N results (default 0){
"data": [
{
"id": "a1b2c3d4-...",
"symbol": "AAPL",
"side": "BUY",
"quantity": 10,
"price": 174.25,
"total": 1742.50, // notional exposure (quantity * price)
"cashUsed": 174.25, // cash actually posted (total / leverage)
"leverage": 10, // 1 for unleveraged trades
"executedAt": "2024-01-15T14:30:00Z",
"source": "ALGORITHM" // MANUAL | ALGORITHM | MCP | LIQUIDATION (/v1 orders are ALGORITHM by default; MCP-server traffic is tagged MCP)
}
],
"pagination": {
"total": 150,
"limit": 100,
"offset": 0
}
}Performance
/portfolios/:id/snapshotsRead OnlyGet historical portfolio value snapshots for performance analysis and charting.
fromISO date stringtoISO date stringinterval1h, 1d, 1w (default 1d)limit1-1000 (default 100)offsetSkip N results (default 0){
"data": [
{
"timestamp": "2024-01-15T00:00:00Z",
"totalValue": 124500.00
},
{
"timestamp": "2024-01-16T00:00:00Z",
"totalValue": 125000.00
}
],
"pagination": {
"total": 30,
"limit": 100,
"offset": 0
}
}Orders
/portfolios/:id/ordersFull AccessPlace a buy or sell order. Supports MARKET, LIMIT, STOP, STOP_LIMIT, TRAILING_STOP, and MARKET_ON_CLOSE order types, with optional leverage (1–100x, PREMIUM/MAX tiers only). Orders are validated against available buying power and maintenance margin before execution.
FREE-tier keys are scoped to MARKET orders only, no leverage, and a daily trade cap (resets 00:00 UTC). Upgrade to Premium/Max (see Billing) to unlock all order types and leverage.
{
"symbol": "AAPL", // Equity ticker, OR crypto pair (BTCUSDT). See Symbology
"side": "BUY", // BUY or SELL
"type": "MARKET", // see per-type fields below
"quantity": 10, // Min 0.0001, up to 8 decimals (prices stay at 4)
"leverage": 1 // Optional, integer 1-100 (PREMIUM/MAX only)
}{
"symbol": "AAPL",
"side": "BUY",
"type": "MARKET",
"quantity": 10,
"leverage": 10 // Optional — posts notional/10 as margin
}{
"symbol": "AAPL",
"side": "BUY",
"type": "LIMIT",
"quantity": 10,
"limitPrice": 170.00, // Required
"leverage": 5 // Optional
}{
"symbol": "NVDA",
"side": "BUY",
"type": "STOP",
"quantity": 10,
"stopPrice": 500.00, // Required
"leverage": 5 // Optional
}{
"symbol": "NVDA",
"side": "BUY",
"type": "STOP_LIMIT",
"quantity": 10,
"stopPrice": 500.00, // Required — trigger
"limitPrice": 502.00, // Required — max price after trigger
"leverage": 2 // Optional
}{
"symbol": "AAPL",
"side": "SELL",
"type": "TRAILING_STOP",
"quantity": 10,
"stopPrice": 180.00, // Required — initial stop
"trailingAmount": 5.00, // Required — dollar or % trail
"trailingType": "AMOUNT" // AMOUNT or PERCENT (default AMOUNT)
}{
"symbol": "SPY",
"side": "BUY",
"type": "MARKET_ON_CLOSE",
"quantity": 5,
"leverage": 10 // Optional
}{
"data": {
"id": "a1b2c3d4-...",
"symbol": "AAPL",
"normalizedSymbol": "AAPL", // Post-normalization (e.g. BTC -> BTCUSDT for crypto)
"assetType": "STOCK", // STOCK | ETF | CRYPTO | FOREX | INDEX | FUTURE | FUND | REIT | ADR | null
"side": "BUY",
"type": "MARKET",
"quantity": 10,
"leverage": 10,
"limitPrice": null,
"stopPrice": null,
"trailingAmount": null,
"trailingType": null,
"status": "FILLED", // or OPEN / TRIGGERED / PARTIALLY_FILLED
"filledQuantity": 10,
"filledPrice": 174.25,
"createdAt": "2024-01-15T14:30:00Z",
"filledAt": "2024-01-15T14:30:01Z",
"algorithmId": "e5f6g7h8-...",
"algorithmName": "my-dca-bot"
}
}/portfolios/:id/ordersRead OnlyList orders with optional status filtering.
statusOPEN, FILLED, CANCELLED, ALLlimit1-100 (default 100)offsetSkip N results (default 0){
"data": [
{
"id": "a1b2c3d4-...",
"symbol": "AAPL",
"side": "BUY",
"type": "LIMIT",
"quantity": 10,
"limitPrice": 170.00,
"stopPrice": null,
"trailingAmount": null,
"trailingType": null,
"leverage": 1, // 1 for unleveraged orders
"source": "ALGORITHM", // MANUAL | ALGORITHM | MCP | LIQUIDATION (/v1 orders are ALGORITHM by default; MCP-server traffic is tagged MCP)
"status": "OPEN",
"filledQuantity": 0,
"filledPrice": null,
"createdAt": "2024-01-15T14:30:00Z",
"filledAt": null
}
],
"pagination": {
"total": 25,
"limit": 100,
"offset": 0
}
}/portfolios/:id/orders/:orderIdFull AccessCancel a pending order. Only orders with status OPEN or TRIGGERED can be cancelled.
{
"data": {
"id": "a1b2c3d4-...",
"status": "CANCELLED",
"message": "Order cancelled successfully"
}
}Webhooks
Receive HMAC-signed HTTP POST notifications when an event happens — alternative to polling list-orders for fill state. Tier-capped: FREE 1, PREMIUM 5, MAX 25.
Supported event types
order.placed— order created (any status). Immediately-filled MARKETs fire this alongsideorder.filled.order.partially_filled— paced LIMIT/STOP slice committed; order still resting. Noisy on big orders; opt in deliberately.order.filled— full completion (terminal).order.cancelled— user-triggered OR automatic (league LOCKED, balance race). Carries optionalcancelReason.position.margin_warning— soft equity drop on a leveraged portfolio.position.margin_call— hard threshold breached; auto-liquidation imminent.position.liquidated— auto-liquidation ran. Payload includes closed-position list.
apiKeyId scope filter is ignored for position.* events — those are always portfolio-level.
/webhooksFull AccessRegister a webhook. The HMAC signing secret is returned ONCE and never read back — capture it from this response. To rotate, delete + recreate.
{
"url": "https://your-app.example.com/mt-webhook", // https only
"events": [ // subset of supported events
"order.placed",
"order.filled",
"order.cancelled",
"position.margin_call",
"position.liquidated"
],
"description": "Production bot", // optional
"apiKeyId": "uuid-of-an-api-key", // optional — scope filter (orders only)
"isActive": true // defaults to true
}{
"data": {
"id": "webhook_uuid",
"url": "https://your-app.example.com/mt-webhook",
"events": ["order.filled"],
"description": "Production bot",
"isActive": true,
"apiKeyId": null,
"lastDeliveryAt": null,
"lastDeliveryStatus": null,
"createdAt": "2026-06-09T...",
"secret": "whsec_abc123..." // CAPTURE THIS — never returned again
}
}apiKeyId is an optional scope filter — when set, the webhook fires ONLY for orders placed via that specific key. Omit to fire for all of the user's orders.
/webhooksRead OnlyList all configured webhooks. The secret is never returned here.
/webhooks/:idFull AccessPartial update — url, events, description, isActive. The signing secret cannot be rotated in place; delete + recreate to rotate.
/webhooks/:idFull AccessDelete the webhook + cascade its delivery audit rows.
Delivery semantics + verification
Each delivery is a POST with these headers (verify all of them):
X-MongoTrader-Event: order.filled
X-MongoTrader-Delivery-Id: 9c1d-… (unique per attempt cluster; dedupe key)
X-MongoTrader-Timestamp: 2026-06-09T17:30:00.000Z
X-MongoTrader-Signature: <base64(hmac_sha256(secret, "<ts>:<body>"))>
Content-Type: application/json
User-Agent: MongoTrader-Webhooks/1.0Receiver verification:
- Reject if the timestamp is more than ~5 minutes off your wall clock (replay protection).
- Compute
expected = base64(hmac_sha256(secret, ts + ":" + raw_body)). - Constant-time compare to
X-MongoTrader-Signature. - Dedupe on
X-MongoTrader-Delivery-Id(hard) ordata.id(soft).
Retry on transient failures: 1m → 5m → 30m → 6h → 18h (≈24h total). After 5 failed attempts the delivery is marked ABANDONED. Failed deliveries roll into the parent webhook's lastDeliveryStatus snapshot.
{
"event": "order.filled",
"deliveredAt": "2026-06-09T17:30:00.000Z",
"data": {
"id": "order_uuid",
"portfolioId": "portfolio_uuid",
"symbol": "AAPL",
"side": "BUY",
"type": "MARKET",
"quantity": 1,
"filledQuantity": 1,
"filledPrice": 174.25,
"status": "FILLED",
"source": "MCP",
"algorithmId": "algo_uuid",
"createdAt": "2026-06-09T...",
"filledAt": "2026-06-09T...",
"cancelReason": "league_locked" // present on order.cancelled ONLY
}
}{
"event": "position.liquidated",
"deliveredAt": "2026-06-09T17:30:00.000Z",
"data": {
"portfolio": {
"id": "portfolio_uuid",
"name": "My Portfolio",
"equity": 1234.56,
"marginLoan": 2000.00,
"maintenanceRequirement": 1200.00,
"marginHealth": 0.027 // (equity - maint) / equity; null if equity <= 0
},
"liquidatedPositions": [ // position.liquidated ONLY
{ "symbol": "AAPL", "quantitySold": 10, "closePrice": 174.25 }
]
}
}Leagues
Discover, create, and enter competitive leagues programmatically with the same API key. The acting user is always the key owner. Reads need a READ_ONLY key; creating and entering leagues need a FULL_ACCESS key.
/v1/leaguesFull AccessCreate a league you own. Always created as a USER-scoped league (API keys cannot create official/ADMIN leagues). Type must be HOURLY, DAILY, WEEKLY, or CUSTOM — CRYPTO_PICK_THREE is admin-only. Times must satisfy openTime < startTime < endTime ≤ closeTime.
{
"name": "My Agent League",
"type": "DAILY",
"openTime": "2026-01-01T00:00:00Z", // registration opens
"startTime": "2026-01-01T00:10:00Z", // registration closes, trading starts
"endTime": "2026-01-02T00:00:00Z", // trading ends
"closeTime": "2026-01-02T01:00:00Z", // final results calculated
"isPublic": true, // optional, default true
"maxEntries": 50, // optional
"entryFee": 0 // optional $MON cost to enter
}/v1/leaguesRead OnlyList leagues visible to the key owner. Optional page / limit query params return a paginated response; includeCompleted=true includes closed leagues the user has a relationship with.
/v1/leagues/:idRead OnlyGet a single league's details. Public leagues are always visible; private leagues require the key owner to have access.
/v1/leagues/:id/crypto-optionsRead OnlyList the selectable crypto symbols for a Crypto Pick Three league — call this to choose the three picks before entering.
/v1/leagues/:id/enterFull AccessEnter a league with a portfolio the key owner controls. Eligibility, capacity, and entry-fee checks are enforced server-side.
{ "portfolioId": "portfolio_123" }/v1/leagues/:id/enter-crypto-pick-threeFull AccessEnter a Crypto Pick Three league with exactly three symbols from the league's allowed set (see crypto-options).
{ "symbols": ["BTC", "ETH", "SOL"] }Billing & Subscription
Upgrade the key owner's account to a paid tier via a Stripe Checkout handoff — the API never takes a card number. The endpoint returns a hosted URL a human opens to pay; the tier flips only after Stripe confirms. Withdrawals (USD payouts) are human-only and are not exposed over the API.
/v1/billing/statusRead OnlyCurrent subscription status for the key owner.
{
"data": {
"tier": "FREE", // FREE | PREMIUM | MAX
"status": "active",
"source": "NONE", // NONE | STRIPE | APPLE_IAP
"cancelAtPeriodEnd": false,
"currentPeriodEnd": null
}
}/v1/billing/checkoutFull AccessStart a paid-tier upgrade. Returns a Stripe Checkout URL (open in a browser to pay) and the session id. Poll GET /v1/billing/status to observe the tier change after payment.
{ "tier": "PREMIUM" } // PREMIUM or MAX{
"data": {
"url": "https://checkout.stripe.com/c/pay/...",
"sessionId": "cs_...",
"message": "Open this URL in a browser to complete payment..."
}
}Rate Limits
Rate limit headers are included in all responses: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
Algorithm Tracking
Track trades made via the External API as "External API Algorithms" so you can see aggregated info (trade count, last trade, portfolios) alongside your internal algorithms in the Algo Builder.
X-Algorithm-Name: my-trading-botRules
- Max 50 characters
- Allowed characters: letters (a-z, A-Z), numbers (0-9), underscore (_), hyphen (-)
- Same name = same algorithm (case-insensitive)
- Maximum 10 unique external algorithms per user
- Without the header, trades are grouped under an "Unnamed API" algorithm for each API key
Behavior
- Algorithms are auto-created on first use of the
X-Algorithm-Nameheader - Algorithms are auto-attached to portfolios when you place orders on them
- Trade statistics (count, last trade) update automatically
- View your external algorithms in the Algo Builder alongside internal algorithms
POST /portfolios/{id}/orders
Headers:
X-API-Key: mt_your_api_key_here
X-Algorithm-Name: my-dca-bot
Content-Type: application/json
{
"symbol": "AAPL",
"side": "BUY",
"type": "MARKET",
"quantity": 10
}{
"data": {
"id": "a1b2c3d4-...",
"symbol": "AAPL",
"side": "BUY",
"type": "MARKET",
"quantity": 10,
"status": "FILLED",
"filledPrice": 174.25,
"algorithmId": "e5f6g7h8-...",
"algorithmName": "my-dca-bot",
...
}
}Error Responses
All errors return a consistent JSON structure with an error code and message.
{
"error": {
"code": "INSUFFICIENT_FUNDS",
"message": "Insufficient cash balance for this order"
}
}HTTP Status Codes
400Invalid request parameters, missing required fields, or malformed JSON
401"API key is required" or "Invalid or expired API key"
403Insufficient permissions, insufficient funds, or rate limit exceeded
404Portfolio or order not found (or not owned by you)
Error Codes
INSUFFICIENT_FUNDSNot enough cash balance to execute the orderINSUFFICIENT_MARGINLeveraged order would breach maintenance margin or exceed postable cash. See subcode and detail for specifics.LEVERAGE_TIER_EXCEEDEDRequested leverage is above the tier cap (FREE=1x, PREMIUM/MAX=100x)PORTFOLIO_LOCKEDPortfolio is locked and does not allow tradingTRADING_FROZENTrading is temporarily frozen on this portfolioORDER_NOT_FOUNDThe specified order does not existCANNOT_CANCEL_ORDEROnly OPEN or TRIGGERED orders can be cancelledRATE_LIMIT_EXCEEDEDToo many requests, includes resetAt timestampEXTERNAL_ALGORITHM_LIMIT_REACHEDMaximum external algorithms (10) per user reachedFREE_TIER_ORDER_TYPE_NOT_ALLOWEDFREE keys can place MARKET orders only — upgrade for LIMIT/STOP/trailing/MOCFREE_TIER_LEVERAGE_NOT_ALLOWEDFREE keys cannot use leverage — upgrade to Premium/MaxFREE_TIER_DAILY_LIMIT_REACHEDFREE keys hit the daily trade cap (resets 00:00 UTC) — includes limit{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again after 2024-01-15T14:31:00Z",
"resetAt": "2024-01-15T14:31:00Z"
}
}{
"error": {
"code": "INSUFFICIENT_MARGIN",
"subcode": "INSUFFICIENT_EQUITY_FOR_MARGIN",
// or "INSUFFICIENT_MARGIN_CAPACITY"
"message": "Order would breach maintenance margin after fill",
"detail": {
"required": 12000.00, // margin needed
"available": 9500.00, // cash / equity usable
"equity": 25000.00,
"maintenanceRequirement": 18500.00,
"grossExposure": 61666.67,
"leverage": 10
}
}
}Example Code
Python
import requests
API_KEY = "mt_your_api_key_here"
BASE_URL = "https://api.mongotrader.com/v1"
headers = {"X-API-Key": API_KEY}
# List portfolios
portfolios = requests.get(f"{BASE_URL}/portfolios", headers=headers).json()
print(portfolios["data"])
# Place a market order with algorithm tracking
order = requests.post(
f"{BASE_URL}/portfolios/{portfolio_id}/orders",
headers={
**headers,
"X-Algorithm-Name": "my-dca-bot" # Optional: track as named algorithm
},
json={
"symbol": "AAPL",
"side": "BUY",
"type": "MARKET",
"quantity": 10
}
).json()
print(order["data"])JavaScript
const API_KEY = "mt_your_api_key_here";
const BASE_URL = "https://api.mongotrader.com/v1";
const headers = { "X-API-Key": API_KEY };
// List portfolios
const portfolios = await fetch(`${BASE_URL}/portfolios`, { headers })
.then(res => res.json());
console.log(portfolios.data);
// Place a market order with algorithm tracking
const order = await fetch(`${BASE_URL}/portfolios/${portfolioId}/orders`, {
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
"X-Algorithm-Name": "my-dca-bot" // Optional: track as named algorithm
},
body: JSON.stringify({
symbol: "AAPL",
side: "BUY",
type: "MARKET",
quantity: 10
})
}).then(res => res.json());
console.log(order.data);