← Home

Developer Docs

Tonpo is an MT5 trading gateway that exposes MetaTrader 5 functionality through a clean REST API and WebSocket interface. This reference covers everything you need to connect, trade, and stream live data.

Base URL
All API requests go to your gateway host. Replace {HOST} with your gateway domain in all examples below.

https://{HOST}

Quick Start

The fastest path from zero to a live trade. Uses the Python SDK — install it first:

bash
pip install tonpo

Then run this — you'll have a live trade executing in under 2 minutes:

python
import asyncio
from tonpo import TonpoClient, TonpoConfig

config = TonpoConfig(
    host="your-gateway-host.com",
    port=443,
    use_ssl=True,
)

async def main():
    # Step 1: Create a Tonpo user (do this once — store the credentials)
    async with TonpoClient.admin(config) as client:
        user = await client.create_user()
        print(f"API key: {user.api_key}")  # Save this!

    # Step 2: Provision an MT5 account (do this once per account)
    async with TonpoClient.for_user(config, user.api_key) as client:
        acct = await client.create_account(
            "12345678",      # MT5 login
            "your-password", # MT5 password (encrypted at rest)
            "ICMarkets-Demo" # Broker server name
        )
        await client.wait_for_active(acct.account_id)  # up to 180s
        print(f"Account ID: {acct.account_id}")  # Save this!

    # Step 3: Trade (use stored api_key + account_id every time)
    async with TonpoClient.for_user(config, user.api_key) as client:
        info = await client.get_account_info()
        print(f"Balance: {info.balance} {info.currency}")

        result = await client.place_market_buy(
            "EURUSD",
            volume=0.10,
            sl=1.0800,
            tp=1.0950,
        )
        print(f"Order ticket: {result.ticket}")

asyncio.run(main())

Authentication

All authenticated endpoints require an API key sent in the X-API-Key header.

bash
curl https://{HOST}/api/users/me \
  -H "X-API-Key: your_api_key_here"

API keys are generated by POST /api/users (no auth required). Store the key securely — it cannot be retrieved after creation, only rotated.

Endpoint typeAuth required
GET /healthNone — public
POST /api/usersNone — public
All other /api/* endpointsX-API-Key header

Users

POST /api/users Public
Create a new Tonpo user. Returns an API key. No authentication required.
Response
{
  "user_id": "usr_01hx...",
  "api_key": "sk_live_..."
}
GET /api/users/me API Key
Get the authenticated user's profile and plan information.
POST /api/users/me/rotate-key API Key
Rotate the current API key. Old key is immediately invalidated. New key returned once.

Accounts

POST /api/accounts API Key
Provision an MT5 account on the gateway. Credentials are encrypted at rest and never returned again.
Request Body
{
  "mt5Login":    "12345678",
  "mt5Password": "your-password",
  "mt5Server":   "ICMarkets-Demo",
  "region":      "eu"  // optional
}
Response
{
  "account_id":  "acc_01hx...",
  "auth_token":  "..."  // one-time token
}
GET /api/accounts API Key
List all accounts belonging to the authenticated user.
GET /api/accounts/{account_id}/status API Key
Get the current connection status of an account.
POST /api/accounts/{account_id}/pause API Key
Pause trading on an account. Keeps the MT5 connection alive but blocks new orders.
POST /api/accounts/{account_id}/resume API Key
Resume a paused account.
DELETE /api/accounts/{account_id} API Key
Permanently deprovision an account. Irreversible — you will need to call POST /api/accounts again.

Trading

GET /api/account API Key
Get live MT5 account info: balance, equity, margin, leverage, currency.
GET /api/positions API Key
Get all currently open positions.
POST /api/orders API Key
Place a new order (market, limit, or stop).
Request Body
{
  "symbol":    "EURUSD",
  "side":      "buy",         // "buy" | "sell"
  "orderType": "market",      // "market" | "limit" | "stop"
  "volume":    0.10,
  "price":     1.0850,        // required for limit/stop
  "sl":        1.0800,        // optional
  "tp":        1.0950,        // optional
  "comment":   "my-bot",      // optional
  "magic":     12345          // optional magic number
}
POST /api/orders/close API Key
Close an open position by ticket. Omit volume to close fully.
POST /api/orders/modify API Key
Modify the SL and/or TP on an open position.
Request Body
{
  "ticket": 123456789,
  "sl":     1.0820,   // optional
  "tp":     1.0960    // optional
}

Market Data

GET /api/symbols API Key
List all symbols available on the connected MT5 account.
GET /api/symbols/{symbol} API Key
Get the current bid/ask price for a symbol. For real-time streaming, use WebSocket.

System

GET /health Public
Gateway health check. Returns status, version, and active connection counts.

Python SDK — Installation

bash
pip install tonpo

Requires Python 3.10+. Dependencies: httpx and websockets — both installed automatically.

TonpoClient

The main SDK class. Two factory methods create the two client modes:

python
from tonpo import TonpoClient, TonpoConfig

config = TonpoConfig(
    host="your-gateway.com",
    port=443,
    use_ssl=True,
)

# Admin client — no auth, use only for create_user()
async with TonpoClient.admin(config) as client:
    user = await client.create_user()

# User client — authenticated, for all trading operations
async with TonpoClient.for_user(config, api_key=user.api_key) as client:
    info = await client.get_account_info()

TonpoConfig options:

FieldDefaultDescription
host"localhost"Gateway hostname
port8080Gateway port
use_sslFalseEnable HTTPS/WSS
request_timeout30.0HTTP request timeout (seconds)
connect_timeout10.0Connection timeout (seconds)
max_reconnect_attempts5WebSocket reconnect limit

SDK — Orders

python
async with TonpoClient.for_user(config, api_key) as client:

    # Market orders
    await client.place_market_buy("EURUSD", volume=0.10, sl=1.0800, tp=1.0950)
    await client.place_market_sell("GBPUSD", volume=0.05, sl=1.2900)

    # Limit orders
    await client.place_limit_buy("EURUSD", volume=0.10, price=1.0820, sl=1.0780)
    await client.place_limit_sell("XAUUSD", volume=0.01, price=2350.00)

    # Stop orders
    await client.place_stop_buy("USDJPY", volume=0.10, price=156.50)
    await client.place_stop_sell("EURUSD", volume=0.10, price=1.0780)

    # Manage positions
    positions = await client.get_positions()
    await client.close_position(ticket=123456789)
    await client.close_position(ticket=123456789, volume=0.05)  # partial
    await client.modify_position(ticket=123456789, sl=1.0820, tp=1.0960)

SDK — WebSocket

Subscribe to real-time ticks and register callbacks for positions, orders, and account updates.

python
async with TonpoClient.for_user(config, api_key) as client:

    # Register callbacks before subscribing
    async def on_tick(tick):
        print(f"{tick.symbol}: bid={tick.bid} ask={tick.ask}")

    async def on_position(pos):
        print(f"Position update: #{pos.ticket} P&L={pos.profit}")

    client.ws.on_tick("EURUSD", on_tick)
    client.ws.on_position(on_position)

    # Start streaming
    await client.subscribe(["EURUSD", "GBPUSD", "XAUUSD"])

    # Keep running
    await asyncio.sleep(3600)

Available WebSocket callbacks:

MethodFires when
ws.on_tick(symbol, cb)New tick arrives for symbol
ws.on_quote(symbol, cb)Bid/ask update for symbol
ws.on_candle(symbol, tf, cb)New candle closes on timeframe
ws.on_position(cb)Any open position changes
ws.on_order_result(cb)Order fill or rejection
ws.on_account(cb)Balance or equity update

Account Lifecycle

MT5 accounts transition through these states after provisioning:

StateDescription
createdAccount provisioned, waiting for node assignment
connectingNode is starting MT5 and logging into broker
activeLogged in and ready for trading
disconnectedConnection lost — auto-reconnect in progress
reconnectingActively attempting to reconnect to broker
login_failedMT5 credentials rejected by broker — check login details
deletedAccount deprovisioned — cannot be reused
Tip: Use await client.wait_for_active(account_id) in the SDK — it polls automatically and raises AccountLoginFailedError if login fails.

WebSocket Protocol

Connect directly to the gateway WebSocket for low-latency real-time data.

Connection URL
wss://{HOST}/ws

Authenticate by sending your API key as a header on connect (or as a query param):

javascript
const ws = new WebSocket(`wss://{HOST}/ws?api_key=${apiKey}`);

ws.onopen = () => {
    // Subscribe to real-time ticks
    ws.send(JSON.stringify({
        type: "subscribe",
        symbols: ["EURUSD", "XAUUSD"],
        request_id: "req_001"
    }));
};

ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    if (msg.type === "tick") {
        console.log(msg.symbol, msg.bid, msg.ask);
    }
};

Incoming message types:

typeDescription
tickBid/ask/last tick for a subscribed symbol
quoteBid/ask update
candleOHLCV candle close
positionOpen position update
orderResultOrder fill or rejection result
accountBalance/equity/margin update
subscribedSubscription confirmed
pongPing response
errorError with code and message
Tick message example
{
  "type":   "tick",
  "symbol": "EURUSD",
  "bid":    1.08542,
  "ask":    1.08556,
  "last":   1.08549,
  "volume": 12,
  "time":   1700000000000
}

Error Handling

All errors return a consistent JSON body:

Error Response
{
  "error":   "Invalid or missing API key",
  "code":    401,
  "type":    "authentication_error"
}
HTTP CodeTypeMeaning
401authentication_errorInvalid or missing API key
403authentication_errorPermission denied — check plan limits
404not_foundAccount or resource not found
429rate_limit_errorToo many requests — back off and retry
500server_errorInternal gateway error

Python SDK exceptions:

ExceptionWhen raised
AuthenticationError401 / 403 response
AccountNotFoundError404 response
AccountLoginFailedErrorMT5 credentials rejected
AccountTimeoutErrorwait_for_active() timed out
TonpoConnectionErrorNetwork / connection failure
TonpoResponseErrorUnexpected HTTP response
TonpoErrorBase class — catch-all

Rate Limits

PlanAPI RequestsTrades / day
Free60 / min10
Basic100 / min50
Pro500 / min200
EnterpriseCustomUnlimited

When a rate limit is exceeded the API returns 429 with a Retry-After header indicating how many seconds to wait.

Security

  • Credentials encrypted at rest — MT5 login and password are AES-256 encrypted on the gateway. They are never returned in any API response after provisioning.
  • TLS everywhere — All HTTP and WebSocket traffic is over TLS 1.2+. Plain HTTP is only for local development.
  • API key authentication — Keys are hashed in the database. If you lose a key, rotate it — it cannot be recovered.
  • Per-user isolation — Each user's accounts are fully isolated. One user cannot access another user's positions or credentials.
  • Key rotation — Rotate your API key at any time via POST /api/users/me/rotate-key. Old key is immediately revoked.

FAQ

Do I need MT5 installed?

No. Tonpo handles the MT5 connection through your gateway. You only need your MT5 broker credentials to provision an account.

How fast is order execution?

Orders go from API call to broker confirmation in under 100ms when the gateway is co-located near your broker's servers.

What happens when my account disconnects?

The gateway automatically attempts to reconnect. Account status moves to reconnecting and then back to active when the connection is restored. Use WebSocket to monitor status in real time.

Can I use multiple accounts?

Yes. Each POST /api/accounts call provisions an additional account. The number of concurrent accounts depends on your plan.

Is there a Python SDK for other languages?

The official SDK is Python. The REST API and WebSocket protocol are standard — you can integrate from any language using plain HTTP requests.

How do I get support?

Email carbon-crumb-churn@duck.com for technical support or open an issue on GitHub.