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.
https://papertrade.world/apiLet 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.
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/apiQuickstart
- 1. Create a free account, then make an API key on the Connect AI page (Keys & apps).
- 2. Save it as
PAPERTRADE_API_KEYand run:
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.
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.
Market status
/marketsWhether 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
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()){
"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
/brokersBrokers 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
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())[
{
"id": "robinhood",
"name": "Robinhood",
"markets": [
"US"
],
"commission": "$0 commission",
"statutoryCharges": true,
"maxLeverage": 2,
"autoSquareOff": null
}
]Search stocks
/instrumentsFind 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
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())[
{
"symbol": "US:AAPL",
"exchange": "US",
"tradingsymbol": "AAPL",
"name": "Apple Inc."
}
]List simulators
/simulatorsAll your simulators, newest first, with clock and funds.
Parameters
None.
Example
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())[
{
"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
/simulatorsA 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
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()){
"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
/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
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()){
"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
/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
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()){
"id": "sim_7e46b23bb2",
"name": "Momentum v2"
}Delete a simulator
/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
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()){
"deleted": "sim_7e46b23bb2"
}Place an order
/simulators/{id}/ordersMARKET 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
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()){
"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
/simulators/{id}/ordersCancel 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
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()){
"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
/simulators/{id}/exitClose 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
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())[
{
"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
}
}
]Add to watchlist
/simulators/{id}/watchlistTrack 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
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()){
"added": [
"US:AAPL",
"US:NVDA"
],
"watchlist": [
"US:TSLA",
"US:AAPL",
"US:NVDA"
]
}Remove from watchlist
/simulators/{id}/watchlistStop 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
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()){
"watchlist": [
"US:TSLA",
"US:NVDA"
]
}Control a replay
/simulators/{id}/clockHistoric 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
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()){
"status": "running",
"now": 1794470400000,
"speed": 30,
"open": 1794468600000,
"close": 1794492000000,
"marketOpen": true
}Get the results report
/simulators/{id}/reportThe 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
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()){
"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.