← Back to the general documentation

API documentation

Trade, read prices and query your account from your own program, with the same rules as the chart. It is simulated money for educational purposes.

Start in 5 minutes

The API is JSON over HTTPS. Amounts and prices travel as text (for example "78000.5") so no precision is lost; dates are Unix seconds. With it you can read prices, open and close positions and query your account from your own program, with the same rules, costs and limits as the chart.

1 · Create the accountPOST /v1/accountsyou get your api_key2 · Read pricesGET /v1/pricespublic, no key3 · Send ordersPOST /v1/ordersmarket or limit4 · CheckGET /v1/accountand /v1/fillsEvery call except the first two carries the X-API-Key header.
Create an account, look at the price, trade and check the result: four calls.
  1. Create a practice account and save the key (it is shown only once):
    curl -X POST https://tapearena.com/v1/accounts
  2. Look at the price (no key needed):
    curl https://tapearena.com/v1/prices
  3. Buy 0.05 BTC at market:
    curl -X POST https://tapearena.com/v1/orders -H "X-API-Key: TU_CLAVE" -H "content-type: application/json" \
      -d '{"symbol":"BTC","side":"buy","qty":"0.05"}'
  4. Look at your account:
    curl https://tapearena.com/v1/account -H "X-API-Key: TU_CLAVE"

A simulated account of 5,000 USDC, maximum leverage 3×, fee 0.035 % and dynamic slippage. Everything is simulated money for educational purposes.

Authentication and keys

  • Every account has one API key. It goes in the X-API-Key header of every call that needs it.
  • The key is created with POST /v1/accounts and is only shown in that response: save it. The server stores only its fingerprint, so it cannot be recovered; if you lose it, create another account.
  • Treat it like a password: do not push it to public repositories or put it in code that runs in a browser.
  • With no key, or a wrong one, the answer is 401 invalid API key.
curl https://tapearena.com/v1/account -H "X-API-Key: your_key_here"

Two ways to get a key

  • Your competition account: open the user menu → My account → API section and press “Generate key”. It is shown only once. You can generate another any time (the old one stops working at once) or revoke it. Orders go into the same account you use on the chart and compete on the leaderboard.
  • A practice account: the ones POST /v1/accounts creates. They are independent of the competition.

A program never needs your password or your web session: only the key.

Concepts worth knowing

  • Symbols: BTC, ETH and SOL (perpetual futures, real market prices).
  • Side and quantity: side is buy or sell; qty is units of the asset (0.05 = 0.05 BTC), not dollars.
  • One net position per symbol. With the API each order adds to or subtracts from the same position. Buying while you are short reduces it or flips it. This differs from the chart, where each order can be an independent trade with its own SL/TP.
  • 0+0.101buy 0.10+0.152buy 0.05-0.053sell 0.20-0.104sell 0.05crosses zero: closes the long(realised P&L) and opens a shortOne net position per symbol: the sum of all your buys and sells.
    With the API each order adds to or subtracts from a single position per asset; there are no independent trades as on the chart.
  • Chart and API on the same account: orders sent through the API make up a net position separate from your chart trades, but both count towards the leverage limit (one cap for everything). To avoid surprises, use one route or the other for each asset.
  • Costs: a 0.035 % fee on the amount of every fill, plus slippage on market orders: 2 basis points plus 1 per 100,000 USDC of amount.
  • Leverage: the total amount of your positions cannot exceed 3× your capital (422 leverage limit exceeded). If your equity falls below 0.5 % of the amount you hold in positions, the system liquidates everything at once.
  • Equity = balance + unrealised profit or loss of the open positions. The balance (balance) already deducts fees.
  • Risk limits: if at the day's close (00:00 UTC) you have lost more than allowed, your positions are closed and the account is blocked (423) until rehabilitated. See the risk limits section.

Reference: account and orders

POST /v1/accounts public, no key

Creates a practice account and returns its API key. The key is only shown here.

ParameterTypeRequiredDescription
countrystring (ISO 3166)noTwo-letter country, only to show the flag.
seasonintegernoId of a season open for enrolment (before it starts).

Example

curl -X POST https://tapearena.com/v1/accounts
import requests
r = requests.post("https://tapearena.com/v1/accounts").json()
api_key = r["api_key"]   # save it

Response

{
  "account": "acc_8bc1ab396b5f",
  "api_key": "cAEFMlBbxnsECOQbifLiaY6BHfXoYFrOYzmYmWqfKp0",
  "balance": "5000",
  "note": "store the key now; it cannot be shown again"
}

Errors

  • 422 · enrolment closed (opens until the season starts) — The season already started: an account can only be enrolled before the start.
  • 422 · name already taken — That name is already taken.
  • 422 · country must be a 2-letter ISO code — The country must be a 2-letter code.
  • 429 · too many accounts created from your address: at most N per M minutes — Too many accounts created from your address (only when the service applies this limit).

GET /v1/prices public, no key

Latest price of every asset. Updated every second.

Example

curl https://tapearena.com/v1/prices

Response

{
  "BTC": "78000",
  "ETH": "2300",
  "SOL": "150"
}

GET /v1/account key required

Your account: balance, equity, whether it is liquidated and your open positions (one per symbol, with a signed quantity: positive = long, negative = short).

Example

curl https://tapearena.com/v1/account -H "X-API-Key: TU_CLAVE"
import requests
H = {"X-API-Key": "TU_CLAVE"}
print(requests.get("https://tapearena.com/v1/account", headers=H).json()["equity"])

Response

{
  "account": "acc_8bc1ab396b5f",
  "balance": "4998.63",
  "equity": "4997.84",
  "liquidated": false,
  "positions": {
    "BTC": {
      "qty": "0.05",
      "entry": "78015.90",
      "sl": null,
      "tp": null
    }
  }
}

Errors

  • 401 · invalid API key — The key is missing or wrong.
  • 429 · rate limit exceeded — Too many requests in a row: wait a moment.

POST /v1/orders key required

Sends a market order (fills at once) or a limit order (fills when the price reaches yours). You can add a Stop Loss and Take Profit, which attach to the position. See the life of an order.

ParameterTypeRequiredDescription
symbolstringyesBTC, ETH or SOL.
sidestringyesbuy or sell.
qtystring (decimal)yesUnits of the asset; greater than 0.
typestringnomarket (default) or limit.
pricestring (decimal)noLimit price. Required when type is limit.
slstring (decimal)noStop Loss: for a long, below the price; for a short, above it.
tpstring (decimal)noTake Profit: for a long, above the price; for a short, below it.

Example

curl -X POST https://tapearena.com/v1/orders -H "X-API-Key: TU_CLAVE" -H "content-type: application/json" \
  -d '{"symbol":"BTC","side":"buy","qty":"0.05","sl":"74000","tp":"84000"}'
import requests
H = {"X-API-Key": "TU_CLAVE"}
r = requests.post("https://tapearena.com/v1/orders", headers=H, json={"symbol": "BTC", "side": "buy", "qty": "0.05"})
print(r.status_code, r.json())

Response

// market order (or an already-executable limit): 201
{
  "status": "filled",
  "symbol": "BTC",
  "side": "buy",
  "qty": "0.05",
  "price": "78015.90",
  "fee": "1.365",
  "realized_pnl": "0"
}

// limit order that waits: 201
{
  "status": "pending",
  "order_id": 1,
  "symbol": "BTC",
  "side": "buy",
  "qty": "0.02",
  "price": "70000"
}

Errors

  • 422 · leverage limit exceeded — The order would take the total amount past the maximum leverage.
  • 422 · no price for symbol — Unknown symbol (use BTC, ETH or SOL).
  • 422 · a limit order needs a price — A limit order needs the price field.
  • 422 · at most 20 pending orders — You already have 20 pending limit orders.
  • 422 · account liquidated — Your account was liquidated and cannot trade.
  • 422 · season not active — Your account's season is not running.
  • 422 · stop loss must be below the current price for a long position — The Stop Loss or Take Profit is on the wrong side of the price (there are equivalent messages for each case).
  • 423 · account blocked: a risk limit was exceeded; rehabilitate it — Your account is blocked for exceeding a risk limit.
  • 429 · rate limit exceeded — Too many requests in a row.
  • 429 · order limit: at most 20 per hour and 200 per day per account (HFT is prohibited) — You reached the maximum orders per hour (20) or per day (200); high-frequency trading is prohibited.

POST /v1/stops key required

Changes or removes the Stop Loss and Take Profit of your position in a symbol. Send null to remove one.

ParameterTypeRequiredDescription
symbolstringyesSymbol with an open position.
slstring | nullnoNew Stop Loss, or null to remove it.
tpstring | nullnoNew Take Profit, or null to remove it.

Example

curl -X POST https://tapearena.com/v1/stops -H "X-API-Key: TU_CLAVE" -H "content-type: application/json" \
  -d '{"symbol":"BTC","sl":"75000","tp":null}'

Response

{
  "symbol": "BTC",
  "sl": "75000",
  "tp": null
}

Errors

  • 422 · no open position in that symbol — You have no open position in that symbol.
  • 422 · stop loss must be above the current price for a short position — Stop Loss or Take Profit on the wrong side of the price (depending on long or short).
  • 423 · account blocked: a risk limit was exceeded; rehabilitate it — Account blocked by risk.

GET /v1/orders/open key required

Your pending limit orders (at most 20 at a time).

Example

curl https://tapearena.com/v1/orders/open -H "X-API-Key: TU_CLAVE"

Response

[
  {
    "id": 1,
    "symbol": "BTC",
    "side": "buy",
    "qty": "0.02",
    "price": "70000",
    "sl": "68000",
    "tp": "76000",
    "created": 1794425798.33,
    "origin": "manual",
    "ref": ""
  }
]

DELETE /v1/orders/{order_id} key required

Cancels a pending limit order.

ParameterTypeRequiredDescription
order_idinteger (path)yesThe order_id the order returned.

Example

curl -X DELETE https://tapearena.com/v1/orders/1 -H "X-API-Key: TU_CLAVE"

Response

{
  "cancelled": 1
}

Errors

  • 404 · unknown order — The order does not exist, already filled or is not yours.

GET /v1/fills key required

Every fill of your account, oldest first. origin says where it came from: api (this API), manual (the chart), conditional (conditional order), strategy (Laboratory strategy) or system (liquidation). realized_pnl is the profit closed in that fill, before deducting the fee.

Example

curl https://tapearena.com/v1/fills -H "X-API-Key: TU_CLAVE"

Response

[
  {
    "symbol": "BTC",
    "side": "buy",
    "qty": "0.05",
    "price": "78015.90",
    "fee": "1.365",
    "realized_pnl": "0",
    "liquidation": false,
    "origin": "api",
    "ref": ""
  }
]

How an order is executed

Market orderPOST /v1/orderstype: "market"fills at onceprice + slippage“filled” responsewith price and feeLimit orderPOST /v1/orderstype: "limit"is the market alreadyat your price?yes“filled” at once, at the current priceno“pending” with an order_idwaits for the price to arriveprice touches it → it fillsin full, no slippageWhile it is pending you can cancel it with DELETE /v1/orders/{id}.
A limit order that is already executable behaves like a market order (but without slippage); otherwise it stays pending.
  • Market: it fills right away at the last price plus slippage. You get status: "filled" with the real price, the fee and, if you closed something, the realized_pnl.
  • Limit: if the market is already at your price or better, it fills at once at the current price and without slippage. Otherwise it stays "pending" and fills, in full, when the price touches it. Market depth and priority queues are not simulated.
  • Stop Loss and Take Profit: they attach to the position. If the price reaches the Stop Loss, the position is closed at market with slippage; at the Take Profit, it is closed at the set price. With a pending limit order they apply when it fills.
  • Leverage: checked when you send and again when a limit order fills; if it does not fit at that moment, the order is dropped.

Reference: public data

These endpoints need no key. They are cached for a few seconds, so there is no point in querying them more often.

GET /v1/seasons public, no key

The seasons, with their rules (minimums, maximum allowed drawdown) and status: upcoming, live or ended.

Example

curl https://tapearena.com/v1/seasons

Response

[
  {
    "id": 1,
    "name": "November 2026",
    "kind": "monthly",
    "start": 1793491200.0,
    "end": 1796083200.0,
    "min_trades": 3,
    "max_drawdown": "0.5",
    "min_active_days": 2,
    "min_trade_frac": "0.01",
    "status": "live"
  }
]

GET /v1/leaderboard public, no key

A season's leaderboard: position, name, return, maximum drawdown, trades, active days and score. With include_unranked=true it adds the accounts that do not qualify yet, with the reason.

ParameterTypeRequiredDescription
limitintegernoHow many rows (1 to 100). Default 10.
seasonintegernoSeason; the current one by default.
include_unrankedbooleannoInclude the unranked.

Example

curl 'https://tapearena.com/v1/leaderboard?limit=5'

GET /v1/participants/{account} public, no key

The public history of an account: statistics, the equity curve (what the leaderboard measures) and the balance curve (the cash with closed trades), positions and trades. Trades of user accounts are published with a 1-hour delay so nobody can copy a live strategy.

ParameterTypeRequiredDescription
rowsintegernoHow many trades (the most recent), up to 50,000. Default 600.
max_pointsintegernoCurve points. Default 600.

Example

curl 'https://tapearena.com/v1/participants/acc_8bc1ab396b5f?rows=50'

GET /v1/curves public, no key

The equity curves of all participants of a season (return against the start), to draw the comparison.

ParameterTypeRequiredDescription
seasonintegernoSeason.
max_pointsintegernoPoints per curve (default 120).
limitintegernoHow many accounts (default 150).

Example

curl 'https://tapearena.com/v1/curves?max_points=60'

GET /v1/benchmarks public, no key

What simply buying and holding each asset would have earned since the season started: the reference line to measure your strategy.

ParameterTypeRequiredDescription
seasonintegernoSeason.

Example

curl https://tapearena.com/v1/benchmarks

Errors and status codes

Errors return a JSON {"detail": "…"}. Body validation errors (for example, side other than buy or sell) return in detail a list with the field and the reason.

CodeMeaningWhat to do
200 / 201Success (201: something was created or filled)—
401Missing or wrong keyCheck the X-API-Key header
403Operation reserved for administratorsNot available to your account
404Unknown order, symbol or resourceCheck the identifier
422Order rejected or invalid body (leverage, SL on the wrong side, season not active…)Read detail and fix it; do not retry as is
423Account blocked for exceeding a risk limitRehabilitate it from the website
429Too many requests, or order limit reachedWait and retry with exponential back-off
502Price data not available right nowRetry in a few seconds

Usage limits

Request bucket (per key)capacity: 20 requests (burst)It refills at 10 requests per secondExample: 30 requests in one instant20 go through (200)10 rejected (429)1On a 429, wait a moment and retry (exponential back-off).2To watch the price you need no more than one query per second: use /v1/prices.
Requests with a key have a burst and a pace limit; public data is cached.
WhatLimit
Requests with a key10 per second per key, with a burst of 20 (429 rate limit exceeded)
Orders (high frequency)20 per hour and 200 per day per account (429 order limit…). High-frequency trading is prohibited
Account creation5 per 60 minutes per address (429)
Pending limit orders20 at a time
Leverage3× equity; liquidation below 0.5 % of the amount
Trades of a participantup to 50,000 per call
Accounts per person1

Good practice: do not query more than once per second; reuse a single connection; on a 429 wait before retrying; do not repeat a rejected order with the same data; keep your key out of your code. Abusive use (automating account creation, overloading the service, exploiting errors or stale prices) may lead to the user being cancelled.

Complete examples

Python with requests: buy, set stops and exit

import requests, time

BASE = "https://tapearena.com"
H = {"X-API-Key": "TU_CLAVE"}

def call(method, path, **kw):
    """Call the API and retry on 429 (exponential back-off)."""
    for wait in (0.2, 0.5, 1, 2, 4):
        r = requests.request(method, BASE + path, headers=H, timeout=10, **kw)
        if r.status_code != 429:
            return r
        time.sleep(wait)
    return r

price = float(requests.get(BASE + "/v1/prices").json()["BTC"])
r = call("POST", "/v1/orders", json={"symbol": "BTC", "side": "buy", "qty": "0.05",
                                       "sl": str(round(price * 0.97)), "tp": str(round(price * 1.06))})
print(r.status_code, r.json())          # 201 {"status": "filled", ...}
print(call("GET", "/v1/account").json()["positions"])

# exit: sell what you hold
pos = call("GET", "/v1/account").json()["positions"]["BTC"]["qty"]
print(call("POST", "/v1/orders", json={"symbol": "BTC", "side": "sell", "qty": pos}).json())

A limit order and cancelling it

o = call("POST", "/v1/orders", json={"symbol": "ETH", "side": "buy", "qty": "1", "type": "limit", "price": "2000"}).json()
if o["status"] == "pending":
    print("waiting, id", o["order_id"])
    call("DELETE", f"/v1/orders/{o['order_id']}")

With the single-file client

from client import Client
c, info = Client.register("https://tapearena.com")      # save info["api_key"]
c.buy("BTC", "0.05")
c.target("BTC", 0.5, leverage=1)       # move the position to 50 % of equity
c.close("BTC")

Watching the price and computing an average

import requests, time
prices = []
for _ in range(30):                       # one price per second
    prices.append(float(requests.get("https://tapearena.com/v1/prices").json()["BTC"]))
    time.sleep(1)
avg = sum(prices) / len(prices)
print("BTC above its 30 s average:", prices[-1] > avg)

JavaScript (fetch)

const H = {"X-API-Key": "TU_CLAVE", "content-type": "application/json"};
const r = await fetch("https://tapearena.com/v1/orders", {method: "POST", headers: H,
  body: JSON.stringify({symbol: "BTC", side: "buy", qty: "0.05"})});
console.log(r.status, await r.json());

These examples use a practice account. Run your programs on your own server or computer, not on a public web page, so you do not expose the key.

Single-file Python client

A single file, no dependencies: copy it next to your program. Download it with curl -O https://tapearena.com/sdk/client.py.

from client import Client, run
c, info = Client.register("https://tapearena.com")     # save info["api_key"]
c.prices(); c.buy("BTC", "0.1"); c.target("BTC", 0.5, leverage=2); c.close("BTC")
run(c, "BTC", lambda hist: 1.0 if hist[-1] > hist[0] else -1.0, every=60)
  • Client.register(url) creates a practice account; Client(url, api_key=…) reuses an existing one.
  • prices(), account(), position(sym), fills(), leaderboard(), seasons().
  • buy(), sell(), order(), target(sym, fraction, leverage=…) (moves the position to a fraction of equity) and close(sym).
  • run(client, symbol, strategy, every=seconds) loops a function that receives the price history and returns a target between −1 and 1.

Available assets

You trade crypto perpetual futures, quoted in USDC with simulated money. Today there are 3 assets; any other symbol is rejected.

API symbolContractHistory (download)Price now
BTCPerpetual future BTC/USDCBTCUSDT…
ETHPerpetual future ETH/USDCETHUSDT…
SOLPerpetual future SOL/USDCSOLUSDT…

Click a symbol to see its chart. You can be long (buy) or short (sell) on each, with a net position per symbol and up to 10x leverage on your total equity. Several assets can be combined in one account.

Historical data

TapeArena does not allow downloading price history: the API only gives current prices (and your program can keep the ones it receives). If you want to test a strategy on history, download it from a public source and work with it on your own machine:

  • Binance Public Data (data.binance.vision): free. Candles (OHLCV) of USDT-M perpetual futures, such as BTCUSDT, ETHUSDT and SOLUSDT, in daily and monthly files since 2020.
  • Tardis.dev: trade-by-trade data, order book and funding of many derivatives markets. Paid, with the first day of each month free to try.

Prices on other markets differ slightly from TapeArena's, so use them for research and do not expect identical results. Check each source's terms of use.

What is not in the API yet

The API covers the basic operation with one net position per symbol. These features exist in the graphical environment and do not yet have an endpoint:

  • Independent trades (closing or reducing a specific trade, per-trade SL/TP).
  • Conditional orders, OCO and expiration.
  • Laboratory strategies (save, enable, portfolio).
  • Rehabilitating a blocked account (done from “My account”).

This list will be updated here when the API grows.

Frequently asked questions

How do I get my API key?

For your competition account, from My account → API → “Generate key”. For a practice account, with POST /v1/accounts. Either way it is shown once: save it.

Why do I get a 429 error?

You exceeded the limit of 10 requests per second per key (burst of 20) or the orders per hour and per day. Wait and retry with exponential back-off.

What is the difference between a market and a limit order?

A market order fills instantly at the last price plus slippage; a limit order fills when the price reaches yours, in full and without slippage.

Can I hold several positions in the same asset through the API?

No: with the API there is one net position per symbol. Buying and selling add to and subtract from that same position.

← General documentation · Laboratory guide · Terms