Documentation · REST API v1

Trade Papertrade from your own code.

A plain JSON API for trading bots, quant scripts and backtests: create simulators, place orders, control replays and pull results, with the same virtual money, fees and market rules as the app. Using Claude, ChatGPT or another AI app? Connect it over MCP instead.

Base URL
https://papertrade.world/api
For your coding agent or app builder
  • Claude Code
  • Codex
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Antigravity
  • Gemini CLI
  • Lovable
  • Bolt
  • Replit
  • v0
  • Builder.io
  • Devin
  • Kiro

Let your AI build the integration

One prompt with every endpoint, parameter, example and error. Paste it into your coding agent or app builder, then say what you want built.

Plain text

About 2400 words. Your API key isn't in it.

Build it with your AI

Most people won't write this by hand, and don't need to. Copy one prompt into Claude Code, Cursor, Codex or any coding agent: it has every endpoint, parameter, example response and error, plus how to build a solid client. Then ask for what you want, like "add paper trading to my bot" or "backtest my strategy on last Friday's NSE session".

About 2400 words. Agents that can browse can also read it from papertrade.world/docs/api/prompt. Your API key isn't in it: the agent reads it from PAPERTRADE_API_KEY.

# Task: integrate the Papertrade REST API

Papertrade (https://papertrade.world) is a paper-trading simulator for 45 stock markets with real broker fees, live prices and replays of past trading days. Everything uses virtual money: nothing reaches a real broker. Use this API to add paper trading to my application (for example a trading bot, a strategy backtester or a dashboard).

Read this whole brief first. Then look at my codebase and build a small, typed Papertrade client module in the language and style it already uses, and wire it into the feature I ask for. If I haven't said what to build, propose a plan and ask before writing a lot of code.

## Basics
- Base URL: https://papertrade.world/api (API version 1, stable: fields may be added, never removed or renamed).
- Auth: every request sends `Authorization: Bearer <key>`. Keys start with `pt_` and are created at https://papertrade.world/mcp#keys. Read the key from an environment variable named `PAPERTRADE_API_KEY`; never hard-code it, log it, commit it or send it to a browser.
- JSON in and out. Send `Content-Type: application/json` with bodies.
- Errors: non-2xx status with body `{ "error": "human-readable message" }`. Surface the message.
- A rejected order is NOT an HTTP error: it returns 200 with `status: "REJECTED"` and a `message` (market closed, not enough margin, after the broker's square-off…). Always check `status`.
- Symbols are `EXCHANGEPREFIX:TICKER`, e.g. `US:AAPL`, `NSE:INFY`, `LSE:HSBA`, `TSE:7203`. Look them up with the search endpoint; a bare ticker uses the simulator's market.
- Times are epoch milliseconds (UTC). In replays they're the replayed day's time, not the wall clock.
- Money is in the simulator's currency (₹ India, $ US, £ UK…). P&L is net of all fees unless labelled gross.
- Live simulators accept orders only while their exchange is open (check `ordersOpen` on the simulator, or the markets endpoint). Historic (replay) simulators trade any time: create one with a past `date`, then play the replay.
- Polling: the get-simulator endpoint returns fresh prices each call; poll at most once per second. There's no websocket.
- Orders placed with an API key are tagged `source: "api"` and the key's name in the user's activity log and reports.

## Endpoints

### Markets

#### Market status: `GET /markets`

Whether each exchange is in session right now, with the reason when it's closed (weekend, holiday, outside hours) and the data source per market. Live simulators accept orders only while their exchange is open.

Example response (trimmed):
```json
{
  "provider": "yahoo",
  "providers": {
    "IN": "yahoo",
    "US": "yahoo",
    "GB": "yahoo",
    "…": "…"
  },
  "sessions": {
    "US": {
      "open": true,
      "today": "2026-11-16",
      "holiday": null,
      "closedReason": null
    },
    "IN": {
      "open": false,
      "today": "2026-11-16",
      "holiday": null,
      "closedReason": "NSE closed at 3:30 PM IST. It opens Tue 17 Nov, 9:15 AM IST."
    }
  }
}
```

#### List brokers: `GET /brokers`

Brokers whose fees can be simulated: commission, whether statutory charges apply, maximum intraday leverage and auto square-off time. Use the id when creating a simulator.

Parameters:
- `market` (query, string): Only brokers for this market id, e.g. "US", "IN", "GB".

Example query: `?market=US`

Example response (trimmed):
```json
[
  {
    "id": "robinhood",
    "name": "Robinhood",
    "markets": [
      "US"
    ],
    "commission": "$0 commission",
    "statutoryCharges": true,
    "maxLeverage": 2,
    "autoSquareOff": null
  }
]
```

#### Search stocks: `GET /instruments`

Find stocks by symbol or company name in one market. Use the returned symbol in watchlist and order calls.

Parameters:
- `q` (query, string, required): Symbol or name, e.g. "apple", "INFY".
- `market` (query, string): Market id (default "IN"), e.g. "US", "GB", "JP".

Example query: `?q=apple&market=US`

Example response (trimmed):
```json
[
  {
    "symbol": "US:AAPL",
    "exchange": "US",
    "tradingsymbol": "AAPL",
    "name": "Apple Inc."
  }
]
```

### Simulators

#### List simulators: `GET /simulators`

All your simulators, newest first, with clock and funds.

Example response (trimmed):
```json
[
  {
    "id": "sim_7e46b23bb2",
    "name": "Momentum",
    "mode": "live",
    "market": "US",
    "broker": "robinhood",
    "leverage": 2,
    "createdAt": 1794501161870,
    "clock": {
      "status": "live",
      "now": 1794666612000,
      "speed": 1,
      "open": 1794666600000,
      "close": 1794690000000,
      "marketOpen": true
    },
    "funds": {
      "capital": 100000,
      "realizedPnl": 74,
      "unrealizedPnl": 412.6,
      "grossPnl": 486.6,
      "charges": 0.36,
      "pnl": 486.24,
      "pnlPct": 0.49,
      "equity": 100486.24,
      "exposure": 39160.2,
      "usedMargin": 19580.1,
      "available": 80906.14,
      "leverage": 2
    },
    "openPositions": 1,
    "orderCount": 8
  }
]
```

#### Create a simulator: `POST /simulators`

A live simulator trades today's prices while the exchange is open. A historic one replays a past trading day: it starts paused at the open (see Control a replay). Every fill pays the chosen broker's fees and the market's taxes.

Parameters:
- `name` (body, string, required): Up to 60 characters.
- `mode` (body, "live" | "historic", required): Live prices, or a replay of a past day.
- `market` (body, string): Market id (default "IN"), e.g. "US", "GB", "DE", "JP", "HK".
- `date` (body, string): Historic only: the trading day to replay, YYYY-MM-DD.
- `capital` (body, number): Starting virtual cash in the market's currency (default about US$100,000 worth).
- `broker` (body, string): Broker id from List brokers (default: the market's first broker; "none" = no fees).
- `leverage` (body, integer): Intraday leverage, 1–5 (default and maximum depend on the market: 5× India, 2× most others).

Example body:
```json
{
  "name": "Momentum",
  "mode": "live",
  "market": "US",
  "capital": 100000,
  "broker": "robinhood"
}
```

Example response (trimmed):
```json
{
  "id": "sim_7e46b23bb2",
  "name": "Momentum",
  "mode": "live",
  "market": "US",
  "broker": "robinhood",
  "leverage": 2,
  "clock": {
    "status": "live",
    "now": 1794666612000,
    "speed": 1,
    "open": 1794666600000,
    "close": 1794690000000,
    "marketOpen": true
  },
  "funds": {
    "capital": 100000,
    "realizedPnl": 0,
    "unrealizedPnl": 0,
    "grossPnl": 0,
    "charges": 0,
    "pnl": 0,
    "pnlPct": 0,
    "equity": 100000,
    "exposure": 0,
    "usedMargin": 0,
    "available": 100000,
    "leverage": 2
  },
  "watchlist": [],
  "positions": [],
  "orders": [],
  "squareOff": null,
  "ordersOpen": true
}
```

#### Get a simulator: `GET /simulators/{id}`

Everything at once: clock, funds (equity, P&L, margin), watchlist prices, positions, orders (newest first), the broker's square-off for today, and whether orders are accepted right now (ordersOpen, with ordersClosedReason when not). Poll this to follow prices; once a second is plenty.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).

Example response (trimmed):
```json
{
  "id": "sim_7e46b23bb2",
  "name": "Momentum",
  "mode": "live",
  "market": "US",
  "clock": {
    "status": "live",
    "now": 1794666612000,
    "speed": 1,
    "open": 1794666600000,
    "close": 1794690000000,
    "marketOpen": true
  },
  "funds": {
    "capital": 100000,
    "realizedPnl": 74,
    "unrealizedPnl": 412.6,
    "grossPnl": 486.6,
    "charges": 0.36,
    "pnl": 486.24,
    "pnlPct": 0.49,
    "equity": 100486.24,
    "exposure": 39160.2,
    "usedMargin": 19580.1,
    "available": 80906.14,
    "leverage": 2
  },
  "watchlist": [
    {
      "symbol": "US:TSLA",
      "name": "Tesla, Inc.",
      "price": {
        "ltp": 392.4,
        "open": 388.1,
        "high": 394,
        "low": 386.5,
        "ref": 386.9,
        "change": 5.5,
        "changePct": 1.42
      }
    }
  ],
  "positions": [
    {
      "symbol": "US:TSLA",
      "name": "Tesla, Inc.",
      "qty": 10,
      "avgPrice": 391.2,
      "ltp": 392.4,
      "realizedPnl": 0,
      "unrealizedPnl": 12,
      "charges": 0,
      "pnl": 12
    }
  ],
  "orders": [
    {
      "id": "ord_af57592f5d",
      "symbol": "US:TSLA",
      "side": "BUY",
      "type": "MARKET",
      "qty": 10,
      "status": "COMPLETE",
      "avgPrice": 391.2,
      "placedAt": 1794666612000,
      "filledAt": 1794666612000,
      "source": "api",
      "agent": "my-trading-bot",
      "bracket": {
        "stopLoss": 383.4,
        "target": 406.85
      },
      "charges": {
        "brokerage": 0,
        "secFee": 0,
        "taf": 0,
        "total": 0
      }
    }
  ],
  "squareOff": null,
  "ordersOpen": true
}
```

#### Rename a simulator: `PATCH /simulators/{id}`

Change a simulator's name.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `name` (body, string, required): New name, up to 60 characters.

Example body:
```json
{
  "name": "Momentum v2"
}
```

Example response (trimmed):
```json
{
  "id": "sim_7e46b23bb2",
  "name": "Momentum v2"
}
```

#### Delete a simulator: `DELETE /simulators/{id}`

Permanently deletes the simulator with its orders, positions and results. This can't be undone.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).

Example response (trimmed):
```json
{
  "deleted": "sim_7e46b23bb2"
}
```

### Trading

#### Place an order: `POST /simulators/{id}/orders`

MARKET fills now. LIMIT rests until the price reaches limitPrice. SL-M (stop-market) waits for triggerPrice, then fills at market; SL (stop-limit) waits for triggerPrice, then rests at limitPrice. A SELL stop's trigger must be below the price, a BUY stop's above. Attach stopLoss and/or target to a MARKET or LIMIT entry and they become exit orders once it fills (one cancels the other). Selling without a position opens a short. A rejected order still returns 200 with status "REJECTED" and a message saying why (e.g. market closed, not enough margin).

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `symbol` (body, string, required): Symbol from Search stocks, e.g. "US:TSLA" or "NSE:INFY". A bare symbol uses the simulator's market.
- `side` (body, "BUY" | "SELL", required): Direction.
- `qty` (body, integer, required): Number of shares, at least 1.
- `type` (body, "MARKET" | "LIMIT" | "SL" | "SL-M"): Default "MARKET".
- `limitPrice` (body, number): LIMIT and SL orders.
- `triggerPrice` (body, number): SL and SL-M orders (optional for SL-M with a trailingStop).
- `stopLoss` (body, number): MARKET/LIMIT entries: exit price if the trade goes against you.
- `target` (body, number): MARKET/LIMIT entries: exit price to take profit.
- `trailingStop` (body, { type: "points" | "percent", value: number }): Trail the stop loss (entries) or the trigger (SL-M) behind the best price.

Example body:
```json
{
  "symbol": "US:TSLA",
  "side": "BUY",
  "qty": 10,
  "type": "MARKET",
  "stopLoss": 383.4,
  "target": 406.85
}
```

Example response (trimmed):
```json
{
  "id": "ord_af57592f5d",
  "symbol": "US:TSLA",
  "side": "BUY",
  "type": "MARKET",
  "qty": 10,
  "status": "COMPLETE",
  "avgPrice": 391.2,
  "placedAt": 1794666612000,
  "filledAt": 1794666612000,
  "source": "api",
  "agent": "my-trading-bot",
  "bracket": {
    "stopLoss": 383.4,
    "target": 406.85
  },
  "charges": {
    "brokerage": 0,
    "secFee": 0,
    "taf": 0,
    "total": 0
  }
}
```

#### Cancel an order: `DELETE /simulators/{id}/orders`

Cancel an OPEN order: a limit, a stop (SL / SL-M), or a bracket's stop-loss or target exit.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `orderId` (query, string, required): The order id, e.g. "ord_9c1d…".

Example query: `?orderId=ord_9c1d2e3f4a`

Example response (trimmed):
```json
{
  "id": "ord_9c1d2e3f4a",
  "symbol": "US:TSLA",
  "side": "BUY",
  "type": "LIMIT",
  "qty": 10,
  "status": "CANCELLED",
  "placedAt": 1794666612000,
  "source": "api",
  "agent": "my-trading-bot",
  "charges": {
    "brokerage": 0,
    "secFee": 0,
    "taf": 0,
    "total": 0
  },
  "limitPrice": 385
}
```

#### Exit positions: `POST /simulators/{id}/exit`

Close one position at market, or every open position when symbol is left out. Open exit orders for those positions are cancelled.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `symbol` (body, string): Close only this position. Leave out to close all.

Example body:
```json
{
  "symbol": "US:TSLA"
}
```

Example response (trimmed):
```json
[
  {
    "id": "ord_5b7e1a90c2",
    "symbol": "US:TSLA",
    "side": "SELL",
    "type": "MARKET",
    "qty": 10,
    "status": "COMPLETE",
    "avgPrice": 395.1,
    "placedAt": 1794666612000,
    "filledAt": 1794666612000,
    "source": "api",
    "agent": "my-trading-bot",
    "charges": {
      "brokerage": 0,
      "secFee": 0,
      "taf": 0,
      "total": 0
    }
  }
]
```

### Watchlist

#### Add to watchlist: `POST /simulators/{id}/watchlist`

Track stocks so their prices come back in Get a simulator. You can trade a stock without adding it first.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `symbols` (body, string[]): Up to 50 symbols, e.g. ["US:AAPL", "US:NVDA"].
- `symbol` (body, string): Or a single symbol.

Example body:
```json
{
  "symbols": [
    "US:AAPL",
    "US:NVDA"
  ]
}
```

Example response (trimmed):
```json
{
  "added": [
    "US:AAPL",
    "US:NVDA"
  ],
  "watchlist": [
    "US:TSLA",
    "US:AAPL",
    "US:NVDA"
  ]
}
```

#### Remove from watchlist: `DELETE /simulators/{id}/watchlist`

Stop tracking a stock. Open positions and orders in it aren't affected.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `symbol` (query, string, required): e.g. "US:AAPL".

Example query: `?symbol=US:AAPL`

Example response (trimmed):
```json
{
  "watchlist": [
    "US:TSLA",
    "US:NVDA"
  ]
}
```

### Replay

#### Control a replay: `POST /simulators/{id}/clock`

Historic simulators only. Play, pause, change speed (1, 5, 10, 30, 60 or 120×) or jump forward. Orders fill at the replay's prices and time. Returns the clock.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
- `action` (body, "play" | "pause" | "speed" | "advance", required): What to do.
- `speed` (body, number): For speed: 1, 5, 10, 30, 60 or 120.
- `minutes` (body, integer): For advance: minutes to jump ahead.

Example body:
```json
{
  "action": "speed",
  "speed": 30
}
```

Example response (trimmed):
```json
{
  "status": "running",
  "now": 1794470400000,
  "speed": 30,
  "open": 1794468600000,
  "close": 1794492000000,
  "marketOpen": true
}
```

### Results

#### Get the results report: `GET /simulators/{id}/report`

The same report as the app's Results: net and gross P&L, charges by type, round-trip trades with win rate and profit factor, day-by-day results (profitable days, best and worst day, streaks), per-stock P&L, peak margin, equity curve and plain-language insights.

Parameters:
- `id` (path, string, required): Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).

Example response (trimmed):
```json
{
  "status": {
    "final": false,
    "label": "Live: results so far"
  },
  "pnl": {
    "net": 1652.66,
    "netPct": 1.65,
    "gross": 1725.24,
    "realized": 1725.24,
    "unrealized": 0,
    "charges": 72.58,
    "equity": 101652.66
  },
  "trades": {
    "closed": 125,
    "open": 0,
    "wins": 70,
    "losses": 55,
    "winRate": 56,
    "profitFactor": 1.12,
    "avgWin": 220.98,
    "avgLoss": -251.22,
    "list": [
      "…"
    ]
  },
  "daily": {
    "traded": 42,
    "green": 24,
    "red": 18,
    "greenRate": 57.14,
    "best": {
      "date": "2026-10-20",
      "net": 1146.01
    },
    "worst": {
      "date": "2026-10-27",
      "net": -871.27
    },
    "streak": {
      "kind": "green",
      "days": 2
    },
    "days": [
      "…"
    ]
  },
  "charges": {
    "total": 72.58,
    "brokerage": 0,
    "secFee": 61.2,
    "taf": 11.38
  },
  "stocks": [
    "…"
  ],
  "equityCurve": [
    "…"
  ],
  "insights": [
    "24 of 42 trading days were profitable; best day 2026-10-20 (+$1,146.01), worst 2026-10-27 (-$871.27)."
  ]
}
```

## Error statuses
- 400: Something in the request is invalid: a missing field, a bad value, or a body that isn't JSON. The message says what.
- 401: No API key, or the key is wrong or deleted.
- 404: That simulator (or order) doesn't exist on your account.
- 409: The simulator was changed by another request at the same moment. Retry.
- 429: Too many requests. Slow down and retry after a moment.
- 502: The market data provider didn't answer. Retry shortly.

## How to build the client
- One function per endpoint you need, with types for requests and responses based on the examples above (treat unknown extra fields as allowed).
- A single request helper that adds the auth header, sets JSON headers, parses JSON, and turns non-2xx responses into an error carrying the status and the `error` message.
- Retry 409, 429 and 502 a few times with backoff; don't retry 400, 401 or 404.
- Treat `status: "REJECTED"` orders as a normal outcome your code handles (log the `message`), not an exception.
- Keep the base URL and key in configuration, so tests can point at a mock.
- Add a short README section on setting `PAPERTRADE_API_KEY`.

Full human-readable reference: https://papertrade.world/docs/api

Quickstart

  1. 1. Create a free account, then make an API key on the Connect AI page (Keys & apps).
  2. 2. Save it as PAPERTRADE_API_KEY and run:
Python
import os, requests

API = "https://papertrade.world/api"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['PAPERTRADE_API_KEY']}"

# 1. A live US simulator with $100,000 and Robinhood's fees
sim = s.post(f"{API}/simulators", json={"name": "My bot", "mode": "live", "market": "US"}).json()

# 2. Buy 10 Apple shares with a stop loss and a target
order = s.post(f"{API}/simulators/{sim['id']}/orders", json={
    "symbol": "US:AAPL", "side": "BUY", "qty": 10, "stopLoss": 260, "target": 285,
}).json()
print(order["status"], order.get("avgPrice") or order.get("message"))

# 3. Later: how is it doing?
report = s.get(f"{API}/simulators/{sim['id']}/report").json()
print("Net P&L:", report["pnl"]["net"], "· win rate:", report["trades"]["winRate"])

Live orders fill only while the exchange is open. To test any time, create a historic simulator and play the replay.

Authentication

Send your API key as a Bearer token on every request. Keys start with pt_, act as you, and can see and trade only your simulators. Create, copy and delete them on the Connect AI page; a deleted key stops working immediately.

Header
Authorization: Bearer pt_YOUR_API_KEY

Keep keys out of source control and client-side code. Use an environment variable or a secrets manager.

Conventions

JSON in, JSON out
Send JSON bodies with Content-Type: application/json. Errors come back as { "error": "message" } with an HTTP status.
Symbols
Exchange prefix and ticker: "US:AAPL", "NSE:INFY", "LSE:HSBA", "TSE:7203". Find them with Search stocks.
Times
Epoch milliseconds (UTC). For replays, times are the replayed day's, not the wall clock.
Money
In the simulator's currency (₹ for India, $ for the US…). P&L is net of every fee unless it says gross.
Who placed it
Orders you place are marked source "api" with your key's name as the agent, in the app's activity and reports.
Polling
Get a simulator returns fresh prices on each call. Once a second is plenty; there's no need to go faster.
Markets

Market status

GET/markets

Whether each exchange is in session right now, with the reason when it's closed (weekend, holiday, outside hours) and the data source per market. Live simulators accept orders only while their exchange is open.

Parameters

None.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.get(f"{API}/markets", headers=HEADERS)
print(r.json())
Response
{
  "provider": "yahoo",
  "providers": {
    "IN": "yahoo",
    "US": "yahoo",
    "GB": "yahoo",
    "…": "…"
  },
  "sessions": {
    "US": {
      "open": true,
      "today": "2026-11-16",
      "holiday": null,
      "closedReason": null
    },
    "IN": {
      "open": false,
      "today": "2026-11-16",
      "holiday": null,
      "closedReason": "NSE closed at 3:30 PM IST. It opens Tue 17 Nov, 9:15 AM IST."
    }
  }
}
Markets

List brokers

GET/brokers

Brokers whose fees can be simulated: commission, whether statutory charges apply, maximum intraday leverage and auto square-off time. Use the id when creating a simulator.

Parameters

marketstringquery
Only brokers for this market id, e.g. "US", "IN", "GB".

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.get(f"{API}/brokers?market=US", headers=HEADERS)
print(r.json())
Response
[
  {
    "id": "robinhood",
    "name": "Robinhood",
    "markets": [
      "US"
    ],
    "commission": "$0 commission",
    "statutoryCharges": true,
    "maxLeverage": 2,
    "autoSquareOff": null
  }
]
Markets

Search stocks

GET/instruments

Find stocks by symbol or company name in one market. Use the returned symbol in watchlist and order calls.

Parameters

qstringqueryrequired
Symbol or name, e.g. "apple", "INFY".
marketstringquery
Market id (default "IN"), e.g. "US", "GB", "JP".

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.get(f"{API}/instruments?q=apple&market=US", headers=HEADERS)
print(r.json())
Response
[
  {
    "symbol": "US:AAPL",
    "exchange": "US",
    "tradingsymbol": "AAPL",
    "name": "Apple Inc."
  }
]
Simulators

List simulators

GET/simulators

All your simulators, newest first, with clock and funds.

Parameters

None.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.get(f"{API}/simulators", headers=HEADERS)
print(r.json())
Response
[
  {
    "id": "sim_7e46b23bb2",
    "name": "Momentum",
    "mode": "live",
    "market": "US",
    "broker": "robinhood",
    "leverage": 2,
    "createdAt": 1794501161870,
    "clock": {
      "status": "live",
      "now": 1794666612000,
      "speed": 1,
      "open": 1794666600000,
      "close": 1794690000000,
      "marketOpen": true
    },
    "funds": {
      "capital": 100000,
      "realizedPnl": 74,
      "unrealizedPnl": 412.6,
      "grossPnl": 486.6,
      "charges": 0.36,
      "pnl": 486.24,
      "pnlPct": 0.49,
      "equity": 100486.24,
      "exposure": 39160.2,
      "usedMargin": 19580.1,
      "available": 80906.14,
      "leverage": 2
    },
    "openPositions": 1,
    "orderCount": 8
  }
]
Simulators

Create a simulator

POST/simulators

A live simulator trades today's prices while the exchange is open. A historic one replays a past trading day: it starts paused at the open (see Control a replay). Every fill pays the chosen broker's fees and the market's taxes.

Parameters

namestringbodyrequired
Up to 60 characters.
mode"live" | "historic"bodyrequired
Live prices, or a replay of a past day.
marketstringbody
Market id (default "IN"), e.g. "US", "GB", "DE", "JP", "HK".
datestringbody
Historic only: the trading day to replay, YYYY-MM-DD.
capitalnumberbody
Starting virtual cash in the market's currency (default about US$100,000 worth).
brokerstringbody
Broker id from List brokers (default: the market's first broker; "none" = no fees).
leverageintegerbody
Intraday leverage, 1–5 (default and maximum depend on the market: 5× India, 2× most others).

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.post(f"{API}/simulators", headers=HEADERS, json={
    "name": "Momentum",
    "mode": "live",
    "market": "US",
    "capital": 100000,
    "broker": "robinhood",
})
print(r.json())
Response
{
  "id": "sim_7e46b23bb2",
  "name": "Momentum",
  "mode": "live",
  "market": "US",
  "broker": "robinhood",
  "leverage": 2,
  "clock": {
    "status": "live",
    "now": 1794666612000,
    "speed": 1,
    "open": 1794666600000,
    "close": 1794690000000,
    "marketOpen": true
  },
  "funds": {
    "capital": 100000,
    "realizedPnl": 0,
    "unrealizedPnl": 0,
    "grossPnl": 0,
    "charges": 0,
    "pnl": 0,
    "pnlPct": 0,
    "equity": 100000,
    "exposure": 0,
    "usedMargin": 0,
    "available": 100000,
    "leverage": 2
  },
  "watchlist": [],
  "positions": [],
  "orders": [],
  "squareOff": null,
  "ordersOpen": true
}
Simulators

Get a simulator

GET/simulators/{id}

Everything at once: clock, funds (equity, P&L, margin), watchlist prices, positions, orders (newest first), the broker's square-off for today, and whether orders are accepted right now (ordersOpen, with ordersClosedReason when not). Poll this to follow prices; once a second is plenty.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.get(f"{API}/simulators/sim_7e46b23bb2", headers=HEADERS)
print(r.json())
Response
{
  "id": "sim_7e46b23bb2",
  "name": "Momentum",
  "mode": "live",
  "market": "US",
  "clock": {
    "status": "live",
    "now": 1794666612000,
    "speed": 1,
    "open": 1794666600000,
    "close": 1794690000000,
    "marketOpen": true
  },
  "funds": {
    "capital": 100000,
    "realizedPnl": 74,
    "unrealizedPnl": 412.6,
    "grossPnl": 486.6,
    "charges": 0.36,
    "pnl": 486.24,
    "pnlPct": 0.49,
    "equity": 100486.24,
    "exposure": 39160.2,
    "usedMargin": 19580.1,
    "available": 80906.14,
    "leverage": 2
  },
  "watchlist": [
    {
      "symbol": "US:TSLA",
      "name": "Tesla, Inc.",
      "price": {
        "ltp": 392.4,
        "open": 388.1,
        "high": 394,
        "low": 386.5,
        "ref": 386.9,
        "change": 5.5,
        "changePct": 1.42
      }
    }
  ],
  "positions": [
    {
      "symbol": "US:TSLA",
      "name": "Tesla, Inc.",
      "qty": 10,
      "avgPrice": 391.2,
      "ltp": 392.4,
      "realizedPnl": 0,
      "unrealizedPnl": 12,
      "charges": 0,
      "pnl": 12
    }
  ],
  "orders": [
    {
      "id": "ord_af57592f5d",
      "symbol": "US:TSLA",
      "side": "BUY",
      "type": "MARKET",
      "qty": 10,
      "status": "COMPLETE",
      "avgPrice": 391.2,
      "placedAt": 1794666612000,
      "filledAt": 1794666612000,
      "source": "api",
      "agent": "my-trading-bot",
      "bracket": {
        "stopLoss": 383.4,
        "target": 406.85
      },
      "charges": {
        "brokerage": 0,
        "secFee": 0,
        "taf": 0,
        "total": 0
      }
    }
  ],
  "squareOff": null,
  "ordersOpen": true
}
Simulators

Rename a simulator

PATCH/simulators/{id}

Change a simulator's name.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
namestringbodyrequired
New name, up to 60 characters.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.patch(f"{API}/simulators/sim_7e46b23bb2", headers=HEADERS, json={"name": "Momentum v2"})
print(r.json())
Response
{
  "id": "sim_7e46b23bb2",
  "name": "Momentum v2"
}
Simulators

Delete a simulator

DELETE/simulators/{id}

Permanently deletes the simulator with its orders, positions and results. This can't be undone.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.delete(f"{API}/simulators/sim_7e46b23bb2", headers=HEADERS)
print(r.json())
Response
{
  "deleted": "sim_7e46b23bb2"
}
Trading

Place an order

POST/simulators/{id}/orders

MARKET fills now. LIMIT rests until the price reaches limitPrice. SL-M (stop-market) waits for triggerPrice, then fills at market; SL (stop-limit) waits for triggerPrice, then rests at limitPrice. A SELL stop's trigger must be below the price, a BUY stop's above. Attach stopLoss and/or target to a MARKET or LIMIT entry and they become exit orders once it fills (one cancels the other). Selling without a position opens a short. A rejected order still returns 200 with status "REJECTED" and a message saying why (e.g. market closed, not enough margin).

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
symbolstringbodyrequired
Symbol from Search stocks, e.g. "US:TSLA" or "NSE:INFY". A bare symbol uses the simulator's market.
side"BUY" | "SELL"bodyrequired
Direction.
qtyintegerbodyrequired
Number of shares, at least 1.
type"MARKET" | "LIMIT" | "SL" | "SL-M"body
Default "MARKET".
limitPricenumberbody
LIMIT and SL orders.
triggerPricenumberbody
SL and SL-M orders (optional for SL-M with a trailingStop).
stopLossnumberbody
MARKET/LIMIT entries: exit price if the trade goes against you.
targetnumberbody
MARKET/LIMIT entries: exit price to take profit.
trailingStop{ type: "points" | "percent", value: number }body
Trail the stop loss (entries) or the trigger (SL-M) behind the best price.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.post(f"{API}/simulators/sim_7e46b23bb2/orders", headers=HEADERS, json={
    "symbol": "US:TSLA",
    "side": "BUY",
    "qty": 10,
    "type": "MARKET",
    "stopLoss": 383.4,
    "target": 406.85,
})
print(r.json())
Response
{
  "id": "ord_af57592f5d",
  "symbol": "US:TSLA",
  "side": "BUY",
  "type": "MARKET",
  "qty": 10,
  "status": "COMPLETE",
  "avgPrice": 391.2,
  "placedAt": 1794666612000,
  "filledAt": 1794666612000,
  "source": "api",
  "agent": "my-trading-bot",
  "bracket": {
    "stopLoss": 383.4,
    "target": 406.85
  },
  "charges": {
    "brokerage": 0,
    "secFee": 0,
    "taf": 0,
    "total": 0
  }
}
Trading

Cancel an order

DELETE/simulators/{id}/orders

Cancel an OPEN order: a limit, a stop (SL / SL-M), or a bracket's stop-loss or target exit.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
orderIdstringqueryrequired
The order id, e.g. "ord_9c1d…".

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.delete(f"{API}/simulators/sim_7e46b23bb2/orders?orderId=ord_9c1d2e3f4a", headers=HEADERS)
print(r.json())
Response
{
  "id": "ord_9c1d2e3f4a",
  "symbol": "US:TSLA",
  "side": "BUY",
  "type": "LIMIT",
  "qty": 10,
  "status": "CANCELLED",
  "placedAt": 1794666612000,
  "source": "api",
  "agent": "my-trading-bot",
  "charges": {
    "brokerage": 0,
    "secFee": 0,
    "taf": 0,
    "total": 0
  },
  "limitPrice": 385
}
Trading

Exit positions

POST/simulators/{id}/exit

Close one position at market, or every open position when symbol is left out. Open exit orders for those positions are cancelled.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
symbolstringbody
Close only this position. Leave out to close all.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.post(f"{API}/simulators/sim_7e46b23bb2/exit", headers=HEADERS, json={"symbol": "US:TSLA"})
print(r.json())
Response
[
  {
    "id": "ord_5b7e1a90c2",
    "symbol": "US:TSLA",
    "side": "SELL",
    "type": "MARKET",
    "qty": 10,
    "status": "COMPLETE",
    "avgPrice": 395.1,
    "placedAt": 1794666612000,
    "filledAt": 1794666612000,
    "source": "api",
    "agent": "my-trading-bot",
    "charges": {
      "brokerage": 0,
      "secFee": 0,
      "taf": 0,
      "total": 0
    }
  }
]
Watchlist

Add to watchlist

POST/simulators/{id}/watchlist

Track stocks so their prices come back in Get a simulator. You can trade a stock without adding it first.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
symbolsstring[]body
Up to 50 symbols, e.g. ["US:AAPL", "US:NVDA"].
symbolstringbody
Or a single symbol.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.post(f"{API}/simulators/sim_7e46b23bb2/watchlist", headers=HEADERS, json={
    "symbols": ["US:AAPL", "US:NVDA"],
})
print(r.json())
Response
{
  "added": [
    "US:AAPL",
    "US:NVDA"
  ],
  "watchlist": [
    "US:TSLA",
    "US:AAPL",
    "US:NVDA"
  ]
}
Watchlist

Remove from watchlist

DELETE/simulators/{id}/watchlist

Stop tracking a stock. Open positions and orders in it aren't affected.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
symbolstringqueryrequired
e.g. "US:AAPL".

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.delete(f"{API}/simulators/sim_7e46b23bb2/watchlist?symbol=US:AAPL", headers=HEADERS)
print(r.json())
Response
{
  "watchlist": [
    "US:TSLA",
    "US:NVDA"
  ]
}
Replay

Control a replay

POST/simulators/{id}/clock

Historic simulators only. Play, pause, change speed (1, 5, 10, 30, 60 or 120×) or jump forward. Orders fill at the replay's prices and time. Returns the clock.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).
action"play" | "pause" | "speed" | "advance"bodyrequired
What to do.
speednumberbody
For speed: 1, 5, 10, 30, 60 or 120.
minutesintegerbody
For advance: minutes to jump ahead.

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.post(f"{API}/simulators/sim_7e46b23bb2/clock", headers=HEADERS, json={"action": "speed", "speed": 30})
print(r.json())
Response
{
  "status": "running",
  "now": 1794470400000,
  "speed": 30,
  "open": 1794468600000,
  "close": 1794492000000,
  "marketOpen": true
}
Results

Get the results report

GET/simulators/{id}/report

The same report as the app's Results: net and gross P&L, charges by type, round-trip trades with win rate and profit factor, day-by-day results (profitable days, best and worst day, streaks), per-stock P&L, peak margin, equity curve and plain-language insights.

Parameters

idstringpathrequired
Simulator id, e.g. "sim_7e46b23bb2" (from List simulators).

Example

Python
import os, requests

API = "https://papertrade.world/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PAPERTRADE_API_KEY']}"}

r = requests.get(f"{API}/simulators/sim_7e46b23bb2/report", headers=HEADERS)
print(r.json())
Response
{
  "status": {
    "final": false,
    "label": "Live: results so far"
  },
  "pnl": {
    "net": 1652.66,
    "netPct": 1.65,
    "gross": 1725.24,
    "realized": 1725.24,
    "unrealized": 0,
    "charges": 72.58,
    "equity": 101652.66
  },
  "trades": {
    "closed": 125,
    "open": 0,
    "wins": 70,
    "losses": 55,
    "winRate": 56,
    "profitFactor": 1.12,
    "avgWin": 220.98,
    "avgLoss": -251.22,
    "list": [
      "…"
    ]
  },
  "daily": {
    "traded": 42,
    "green": 24,
    "red": 18,
    "greenRate": 57.14,
    "best": {
      "date": "2026-10-20",
      "net": 1146.01
    },
    "worst": {
      "date": "2026-10-27",
      "net": -871.27
    },
    "streak": {
      "kind": "green",
      "days": 2
    },
    "days": [
      "…"
    ]
  },
  "charges": {
    "total": 72.58,
    "brokerage": 0,
    "secFee": 61.2,
    "taf": 11.38
  },
  "stocks": [
    "…"
  ],
  "equityCurve": [
    "…"
  ],
  "insights": [
    "24 of 42 trading days were profitable; best day 2026-10-20 (+$1,146.01), worst 2026-10-27 (-$871.27)."
  ]
}

Errors

Errors return a JSON body like {"error": "Simulator \"sim_x\" not found."}. A rejected order isn't an error: it returns 200 with status: "REJECTED" and a message.

400
Something in the request is invalid: a missing field, a bad value, or a body that isn't JSON. The message says what.
401
No API key, or the key is wrong or deleted.
404
That simulator (or order) doesn't exist on your account.
409
The simulator was changed by another request at the same moment. Retry.
429
Too many requests. Slow down and retry after a moment.
502
The market data provider didn't answer. Retry shortly.

Versioning

This is version 1, stable since 14 November 2026. We may add endpoints, optional fields and new values, so ignore fields you don't know. We won't remove or rename fields, or change what they mean, without a new version and notice. Questions or requests: contact@papertrade.world.