# MetaRPC MT4 / MT5 API: instructions for AI agents

MetaRPC hosts MetaTrader 4 and MetaTrader 5 terminals in the cloud and puts a REST API, WebSocket streams and a gRPC API in
front of them. Every trading account you connect runs in its own MetaTrader terminal. Your code needs one API key; it does not
need a MetaTrader installation. This page contains everything a coding agent needs in one file. The links point to the full
guides.

Site: <https://mrpc.pro> · Developer hub: <https://mrpc.pro/sandbox-apis> · Get an API key: <https://mrpc.pro/signup>

---

## 1. Endpoints (production)

| | MT5 | MT4 |
|---|---|---|
| REST base URL | `https://mt5.mrpc.pro` | `https://mt4.mrpc.pro` |
| WebSocket streams | `wss://mt5.mrpc.pro/<Stream>` | `wss://mt4.mrpc.pro/<Stream>` |
| gRPC (TLS) | `mt5.mrpc.pro:443`, package `mt5_term_api` | `mt4.mrpc.pro:443`, package `mt4_term_api` |
| API console (every endpoint, runnable) | <https://mt5.mrpc.pro/apiui> | <https://mt4.mrpc.pro/apiui> |
| OpenAPI 3.0 | <https://mrpc.pro/openapi/mt5.json> | <https://mrpc.pro/openapi/mt4.json> |
| Protobuf definitions | <https://mrpc.pro/proto/mt5/client.zip> | <https://mrpc.pro/proto/mt4/client.zip> |

## 2. Authentication: two headers

| Header | Value | When |
|---|---|---|
| `APIKey` | your API key from the portal (<https://mrpc.pro/my>) | every call, including `Connect` / `ConnectEx` and `Disconnect` |
| `id` | the terminal ID: the `terminalInstanceGuid` returned by `Connect` / `ConnectEx` | every call after connecting |

- The API key and the terminal ID are different values. The key identifies whoever pays. The terminal ID identifies the
  connected trading account.
- gRPC: send both as metadata. gRPC sends metadata names in lower case (`apikey`, `id`).
- Browsers cannot set WebSocket headers. A browser passes `id` and `apikey` in the query string of a WebSocket URL instead.
- Try it without an account: the live demo on <https://mrpc.pro/sandbox-apis> runs guests with the key `TRIAL`. Trial limits
  apply, and a trial terminal lives at most 15 minutes. Runs that use your own key are paid from your balance.

## 3. Connect and disconnect

`ConnectEx` connects by the broker server name, exactly as MetaTrader shows it. It waits until the terminal is connected and
returns the terminal ID:

```bash
curl "https://mt5.mrpc.pro/ConnectEx?user=50123456&password=YOUR_PASSWORD&mtClusterName=MetaQuotes-Demo&timeoutSeconds=120" \
  -H "APIKey: YOUR_API_KEY"
```

```json
{"data": {"terminalInstanceGuid": "mt5_live_6ee091c13e7f4e6ea7bf53273a020583", "terminalType": "MT5"}}
```

| Call | Parameters (query string) |
|---|---|
| `ConnectEx` | `user`, `password`, `mtClusterName` (broker server name); optional `timeoutSeconds` (default 120), `baseChartSymbol`, `expertsToAdd`, `name`, `expiration` (terminal lifetime in minutes, 0 = no limit) |
| `Connect` | `user`, `password`, `host` (IP address or DNS name of the MT server), `port`; same optional parameters as `ConnectEx` |
| `CheckConnect` | none (send the `id` header): is the terminal's connection alive |
| `Disconnect` | none (send the `id` header); optional `reason` |

MT4 has the same calls at `https://mt4.mrpc.pro` (example server: `TeleTrade-Demo`).

**Always disconnect.** A connected terminal is billed until it stops. Call `Disconnect` in a `finally` block, including when
a call fails. If `ConnectEx` returns an error and no terminal ID, do not go on to other calls.

## 4. REST: shape of every call

- Every REST call is a `GET` on `<base URL>/<Method>`. Parameters go in the query string, for example
  `GET https://mt5.mrpc.pro/SymbolInfoTick?symbol=EURUSD`. Some methods have a path, such as `QuoteHistory/Bars` or `Logs/OnJournal`.
- Replies are JSON with HTTP 200: `{"data": {...}}` on success, or `{"error": {...}}` on failure (see section 7).
- Times are in broker (server) time. Each time has a `...Utc` sibling, for example `serverTime` and `serverTimeUtc`.
- Enumerations accept either the name or the number (`TMT5_ORDER_TYPE_BUY` or `0`).

### MT5 essentials (`https://mt5.mrpc.pro`)

| Purpose | Call |
|---|---|
| Account | `AccountSummary` |
| Price | `SymbolInfoTick?symbol=EURUSD`; also `SymbolParamsMany`, `SymbolInfoDouble`, `QuoteHistory/Bars`, `QuoteHistory/Ticks`, `MarketBookAdd` then `MarketBookGet` |
| Trade | `OrderSend?symbol=&operation=&volume=`; optional `price` (pending orders), `stopLoss`, `takeProfit`, `slippage`, `comment`, `expertId` (magic number), `stopLimitPrice` |
| Order types (`operation`) | `TMT5_ORDER_TYPE_BUY` 0, `_SELL` 1, `_BUY_LIMIT` 2, `_SELL_LIMIT` 3, `_BUY_STOP` 4, `_SELL_STOP` 5, `_BUY_STOP_LIMIT` 6, `_SELL_STOP_LIMIT` 7 |
| Open positions and pending orders | `OpenedOrders` (`positionInfos`, `openedOrders`) |
| Modify SL/TP | `OrderModify?ticket=&stopLoss=&takeProfit=` |
| Close or partly close a position; delete a pending order | `OrderClose?ticket=&volume=&slippage=` |
| History | `PositionsHistory`, `OrderHistory` (page with `pageSize`, `pageToken`); statistics `TradeStats` |

`OrderSend` returns `returnedCode` / `returnedStringCode` (`10009` / `TRADE_RETCODE_DONE` on success), `deal`, `order`
(the ticket of the new position), `volume` and `price`.

### MT4 essentials (`https://mt4.mrpc.pro`)

| Purpose | Call |
|---|---|
| Account | `AccountSummary` |
| Price | `Quote?symbol=EURUSD`, `QuoteMany?symbols=EURUSD&symbols=GBPUSD`, `Symbols`, `QuoteHistory/Bars` |
| Trade | `OrderSend?symbol=&operationType=&volume=`; optional `price`, `stoploss`, `takeprofit`, `slippage`, `comment`, `magicNumber` |
| Order types (`operationType`) | `OC_OP_BUY` 0, `OC_OP_SELL` 1, `OC_OP_BUYLIMIT` 2, `OC_OP_SELLLIMIT` 3, `OC_OP_BUYSTOP` 4, `OC_OP_SELLSTOP` 5 |
| Open and pending orders | `OpenedOrders` (`orderInfos`) |
| Modify | `OrderModify?orderTicket=&newStopLoss=&newTakeProfit=` |
| Close or delete | `OrderCloseDelete?orderTicket=` (`lots` smaller than the order closes part of it; the remaining part gets a new ticket); `OrderCloseBy` |
| History | `OrdersHistory?inputFrom=&pageSize=` (continue with `pageToken` = the reply's `nextPageToken`); statistics `TradeStats` |

The full list of calls and parameters is in the API console and in the OpenAPI files (section 1).

### Minimal client (Python, REST)

```python
import os
import requests

BASE = os.environ.get("MRPC_URL", "https://mt5.mrpc.pro")
HEADERS = {"APIKey": os.environ["MRPC_API_KEY"]}


def call(path, **params):
    body = requests.get(f"{BASE}/{path}", params=params, headers=HEADERS, timeout=180).json()
    if body.get("error"):
        raise RuntimeError(body["error"])
    return body["data"]


HEADERS["id"] = call("ConnectEx", user=os.environ["MT5_LOGIN"], password=os.environ["MT5_PASSWORD"],
                     mtClusterName=os.environ["MT5_SERVER"], timeoutSeconds=120)["terminalInstanceGuid"]
try:
    account = call("AccountSummary")
    print(account["accountBalance"], account["accountCurrency"])
finally:
    call("Disconnect")
```

## 5. gRPC: shape of every call

- Open a TLS channel to `mt5.mrpc.pro:443` (package `mt5_term_api`) or `mt4.mrpc.pro:443` (package `mt4_term_api`).
- Every REST call has the same name over gRPC. The calls are grouped into services, for example `Connection` (`ConnectEx`,
  `Disconnect`), `AccountHelper` (`AccountSummary`), `MarketInfo` (`SymbolInfoTick` on MT5, `Quote` on MT4), `TradingHelper`
  (`OrderSend`, `OrderClose`) and `SubscriptionService` (`OnSymbolTick`).
- Metadata: `apikey` on every call, and `id` on every call after `ConnectEx`.
- Each reply holds either `data` or `error`. Check `error` before you use `data`.
- Streams are server streams. They run until you cancel them.

```python
import grpc
from MetaRpcMT5 import mt5_term_api_connection_pb2 as connection, mt5_term_api_connection_pb2_grpc as connection_grpc

channel = grpc.secure_channel("mt5.mrpc.pro:443", grpc.ssl_channel_credentials())
reply = connection_grpc.ConnectionStub(channel).ConnectEx(connection.ConnectExRequest(
    user=50123456, password="YOUR_PASSWORD", mt_cluster_name="MetaQuotes-Demo", timeout_seconds=120),
    metadata=[("apikey", "YOUR_API_KEY")])
md = [("apikey", "YOUR_API_KEY"), ("id", reply.data.terminal_instance_guid)]
```

## 6. Real-time data: streams, not polling

Each stream is a WebSocket on its REST route (`wss://mt5.mrpc.pro/OnSymbolTick?symbolNames=EURUSD,GBPUSD`) with the same
`APIKey` and `id` headers. Over gRPC each stream is a server stream with the same name. Each WebSocket text frame is one JSON
object, `{"data": {...}}` or `{"error": {...}}`.

| Stream | Platforms | Sends |
|---|---|---|
| `OnSymbolTick?symbolNames=EURUSD,GBPUSD` | MT4, MT5 | every tick of the symbols; one stream can carry many symbols |
| `OnTrade` | MT4, MT5 | orders, deals and positions as they change |
| `OnTradeTransaction` | MT5 | trade transactions as the terminal receives them |
| `OnPositionProfit?timerPeriodMilliseconds=1000` (MT5) / `OnOpenedOrdersProfit?timerPeriodMilliseconds=1000` (MT4) | MT4, MT5 | floating profit, periodically |
| `OnPositionsAndPendingOrdersTickets?timerPeriodMilliseconds=1000` (MT5) / `OnOpenedOrdersTickets?pullIntervalMilliseconds=1000` (MT4) | MT4, MT5 | tickets of open positions and pending orders |
| `OnConnectState` | MT4, MT5 | the terminal's connection state (Connected, Syncing, ...) |
| `Logs/OnJournal`, `Logs/OnExperts` | MT4, MT5 | the terminal's journal and Experts log, live |

Polled calls (`SymbolInfoTick`, `Quote`, `AccountSummary`, `OpenedOrders`) wait in the same queue as orders (at most 20
requests waiting, `TERMINAL_BUSY`) and count toward the 300 calls a minute. Orders do not count toward that limit, so polling
never gets an order refused by it. Streams run separately, so a burst of orders never delays them. Read quotes, positions and
trades from streams.

## 7. Replies and error handling

Every reply has HTTP status 200 and contains either `data` or `error`:

```json
{"error": {
  "type": "MRPC",
  "errorCode": "TERMINAL_RATE_LIMIT_EXCEEDED",
  "errorMessage": "...",
  "properties": [{"errorArgKey": "RetryAfterSeconds", "errorArgValue": "12"}]
}}
```

- Branch on `errorCode`, not on `errorMessage`. Additional values such as `RetryAfterSeconds` come in `properties`, a list of
  `errorArgKey` / `errorArgValue` pairs.
- Errors that come from MetaTrader also fill `mqlErrorCode` / `mqlErrorIntCode` / `mqlErrorDescription`, and
  `mqlErrorTradeCode` / `mqlErrorTradeIntCode` / `mqlErrorTradeDescription` for trade errors.
- A refused API key is the exception: `API_KEY_INVALID` / `API_KEY_EXPIRED` come with HTTP 401, and nothing is charged.
- On MT5, a trade result that is not `TRADE_RETCODE_DONE` is an order result, not an API failure. For example, the broker can
  refuse a burst itself with `TRADE_RETCODE_TOO_MANY_REQUESTS`.

| `errorCode` | Meaning | What to do |
|---|---|---|
| `API_KEY_INVALID`, `API_KEY_EXPIRED` (HTTP 401) | the `APIKey` header is wrong or has expired | fix the key; retrying unchanged never succeeds |
| `TERMINAL_INSTANCE_NOT_FOUND` | the `id` is not a live terminal | connect again and use the new terminal ID |
| `BROKER_SERVER_NOT_FOUND` | `mtClusterName` does not match any known broker server | use the server name exactly as MetaTrader shows it |
| `TERMINAL_RATE_LIMIT_EXCEEDED` | more than 300 calls in the last minute on this terminal (trading calls are not counted); nothing was done | wait `RetryAfterSeconds`, then send again |
| `TERMINAL_BUSY` | 20 requests are already waiting on this terminal, or the queue cannot serve the request in time; nothing was done | retry after `RetryAfterSeconds`; read quotes and positions from streams instead of polling |
| `TOO_MANY_ORDERS_IN_QUEUE` | 5 orders are already waiting; this order was **not placed and will never execute** | wait for the earlier orders' answers; send it again only if it is still wanted |
| `TOO_MANY_TICK_STREAMS` | 15 `OnSymbolTick` streams are already open on this terminal | cancel streams you no longer read (a client that reconnects must cancel its old stream first), or put more symbols in one stream |
| `TOO_MANY_SYMBOLS_IN_MARKET_WATCH` | all 50 Market Watch symbols are in use, or a subscription would cover a 51st symbol; nothing was done | close a tick stream or subscribe to fewer symbols first; retrying unchanged fails the same way |
| `COMMAND_TIMEOUT_EXCEEDED` (MT5), `COMMAND_TIMEOUT_EXEEDED` (MT4, spelled exactly like this) | the call did not finish within its timeout (section 9) | for an order: the outcome is unknown, so reconcile before you resend (section 9) |

## 8. Limits (per terminal ID)

Limits apply to each terminal, that is, to each connected trading account. Reads and orders share the queue of 20 waiting
requests; the calls a minute count everything except trading calls. A request over a limit is refused at once with a reply
error (HTTP 200) and is **not executed**. Nothing is throttled and no connection is dropped.

| Limit | Value | Over the limit |
|---|---|---|
| API calls | **300 per sliding minute**. Opening a stream counts as one call; the data a stream delivers does not count; `Connect` / `ConnectEx` / `Disconnect` do not count. Trading calls (`OrderSend`, `OrderSendWithoutChecks`, `OrderModify`, `OrderClose`, `OrderCloseDelete`, `OrderCloseBy`) do not count and are never refused by this limit; orders are limited only by the order queue (5 waiting) | `TERMINAL_RATE_LIMIT_EXCEEDED` with `RetryAfterSeconds` (seconds until the oldest call leaves the minute) |
| Requests of any kind waiting | **20** | `TERMINAL_BUSY` with `RetryAfterSeconds` |
| Orders waiting for execution | **5** | `TOO_MANY_ORDERS_IN_QUEUE` with `RetryAfterSeconds`; the order is never executed |
| `OnSymbolTick` streams open at once | **15** (one stream can carry many symbols) | `TOO_MANY_TICK_STREAMS` |
| Symbols subscribed at once (all tick streams together) | **50** | `TOO_MANY_SYMBOLS_IN_MARKET_WATCH` |
| Symbols in Market Watch | **50**. A call that needs a new symbol removes the symbol used longest ago. A symbol that is streamed, held in a position or order, or shown on a chart is never removed | `TOO_MANY_SYMBOLS_IN_MARKET_WATCH` when all 50 are in use |

There are **no limits per API key or per IP address**. One key can run any number of terminals.

Until 2026-10-15 the call limit answered with HTTP 429 (gRPC `RESOURCE_EXHAUSTED`) and a `Retry-After` header. Handle that
form the same way as the reply error: wait, then retry.

## 9. Timeouts, and reconciling orders

| Operation | Timeout | Error |
|---|---|---|
| MT5 `AccountSummary`, `OpenedOrders`, `SymbolsList` | 15 s | `COMMAND_TIMEOUT_EXCEEDED` |
| Every other MT5 call (trading and history included) | 30 s | `COMMAND_TIMEOUT_EXCEEDED` |
| Every MT4 call | 30 s | `COMMAND_TIMEOUT_EXEEDED` |
| `Connect`, `ConnectEx` | the request's `timeoutSeconds` (default 120 s) | a connect error |

The timeout counts from the moment the request enters the terminal's queue, so time spent waiting behind other requests counts.

**A timed-out order can still execute.** A timeout ends the wait, not the command, so the terminal can still execute the order a
moment later. Until you check, the outcome is unknown. Before you send it again:

1. give every order attempt a unique `expert_id` (`expertId` in MT5 REST, `magicNumber` in MT4 REST);
2. look for it in `OnTrade`, `OpenedOrders` and the order / deal history of the last minutes;
3. if you find it, the order was executed: use its ticket. If you do not, it was not executed: resend it with a new `expert_id`.

Use `expert_id` as the key, not the comment: some brokers overwrite comments. Keep at most one order in flight per account.

## 10. Throughput

- Each terminal processes commands with **two workers in parallel**, on MT4 and MT5. A slow request (history, a slow order)
  does not hold up the next one, and two orders can execute at the same time. Requests for one account queue in the order
  they arrive.
- For more throughput on one trading account, connect it a second time with a different terminal ID. Each terminal has its
  own two workers and its own limits, so you can spread orders over both. **Each terminal is billed separately.**

## 11. SDKs (open source, <https://github.com/MetaRPC>)

Each SDK ships the generated gRPC clients. These install commands were tested; for the other languages, follow the
repository's docs.

| Language | MT5 | MT4 | Trade Copier | Install (tested) |
|---|---|---|---|---|
| Python | [PyMT5](https://github.com/MetaRPC/PyMT5) | [PyMT4](https://github.com/MetaRPC/PyMT4) | [PyCopier](https://github.com/MetaRPC/PyCopier) | `pip install MetaRpcMT5` / `pip install MetaRpcMT4` |
| JavaScript / Node.js / TypeScript | [NodeMT5](https://github.com/MetaRPC/NodeMT5) | [NodeMT4](https://github.com/MetaRPC/NodeMT4) | [NodeCopier](https://github.com/MetaRPC/NodeCopier) | `npm install github:MetaRPC/NodeMT5 @grpc/grpc-js` (MT4: `NodeMT4`); not on npm |
| C# / .NET | [CSharpMT5](https://github.com/MetaRPC/CSharpMT5) | [CSharpMT4](https://github.com/MetaRPC/CSharpMT4) | [CSharpCopier](https://github.com/MetaRPC/CSharpCopier) | `dotnet add package MetaRPC.MT5` / `MetaRPC.MT4` |
| Go | [GoMT5](https://github.com/MetaRPC/GoMT5) | [GoMT4](https://github.com/MetaRPC/GoMT4) | [GoCopier](https://github.com/MetaRPC/GoCopier) | `go get github.com/MetaRPC/GoMT5/package` (MT4: `GoMT4/package`) |
| Java | [JavaMT5](https://github.com/MetaRPC/JavaMT5) | [JavaMT4](https://github.com/MetaRPC/JavaMT4) | [JavaCopier](https://github.com/MetaRPC/JavaCopier) | not on Maven Central: clone the repository, run `mvn install -DskipTests` (JavaMT5, JavaCopier) or `./gradlew publishToMavenLocal` (JavaMT4), then depend on `pro.mrpc:mt5:1.0.0` / `pro.mrpc:mt4:1.0.0` / `pro.mrpc:copier:1.0.0` |
| C++ | [CppMT5](https://github.com/MetaRPC/CppMT5) | [CppMT4](https://github.com/MetaRPC/CppMT4) | [CppCopier](https://github.com/MetaRPC/CppCopier) | preview only: the MT4/MT5 SDK returns simulated results and does not trade (the Trade Copier SDK is real); see the repository's `docs` folder |
| Rust | [RustMT5](https://github.com/MetaRPC/RustMT5) | [RustMT4](https://github.com/MetaRPC/RustMT4) | [RustCopier](https://github.com/MetaRPC/RustCopier) | preview only: the MT4/MT5 SDK returns simulated results and does not trade (the Trade Copier SDK is real); see the repository's `docs` folder |
| Ruby | [RubyMT5](https://github.com/MetaRPC/RubyMT5) | [RubyMT4](https://github.com/MetaRPC/RubyMT4) | [RubyCopier](https://github.com/MetaRPC/RubyCopier) | preview only: the MT4/MT5 SDK returns simulated results and does not trade (the Trade Copier SDK is real); see the repository's `docs` folder |
| PHP | no SDK: call REST with `curl` | | | see the PHP examples below |

Each repository's documentation is in its `docs` folder, for example <https://github.com/MetaRPC/PyMT5/tree/main/docs>.

### Complete examples on the site

| | MT5 | MT4 |
|---|---|---|
| REST quickstart (connect, account, prices, orders, positions, history, streams, disconnect) | [guide](https://mrpc.pro/docs-legacy/mt5-client-api) · [raw .md](https://mrpc.pro/_content/mrpc.shared/docs/en/mt5-client-api.md) | [guide](https://mrpc.pro/docs-legacy/mt4-client-api) · [raw .md](https://mrpc.pro/_content/mrpc.shared/docs/en/mt4-client-api.md) |
| Python (gRPC) | [page](https://mrpc.pro/mt5-client-grpc-example-for-python) | [page](https://mrpc.pro/mt4-client-for-python-example) |
| C# (gRPC) | [page](https://mrpc.pro/mt5-client-grpc-example-for-csharp) | [page](https://mrpc.pro/mt4-client-grpc-example-for-c) |
| Node.js (gRPC) | [page](https://mrpc.pro/mt5-client-grpc-example-for-node-js) | [page](https://mrpc.pro/mt4-client-grpc-example-for-node-js) |
| TypeScript (gRPC) | [page](https://mrpc.pro/js-typescript-mt5-grpc-api-consuming-example) | [page](https://mrpc.pro/js-typescript-mt4-grpc-api-consuming-example) |
| Browser JavaScript (REST + WebSocket) | [page](https://mrpc.pro/mt5-javascript-typescript-grpc-client-from-browser-webpack) | [page](https://mrpc.pro/mt4-javascript-typescript-grpc-client-from-browser-webpack) |
| Go (gRPC) | [page](https://mrpc.pro/mt5-client-grpc-example-for-golang) | [page](https://mrpc.pro/mt4-client-grpc-example-for-golang) |
| Java (gRPC) | [page](https://mrpc.pro/mt5-client-grpc-example-for-java-app) | [page](https://mrpc.pro/mt4-client-grpc-example-for-java-app) |
| PHP (REST) | [page](https://mrpc.pro/mt5-client-php8-1-example) | [page](https://mrpc.pro/mt4_client_php81_example) |
| Interactive explorer | [MT5 explorer](https://mrpc.pro/explorer/mt5-client) | [MT4 explorer](https://mrpc.pro/explorer/mt4-client) |

Live demo in Python, JavaScript, C#, Go, Java, C++, Rust and Ruby: <https://mrpc.pro/sandbox-apis#live-code>. The MT5 demo
programs can be downloaded: [Python](https://mrpc.pro/demo/mt5_demo.py), [JavaScript](https://mrpc.pro/demo/mt5_demo.mjs),
[C#](https://mrpc.pro/demo/mt5_demo.cs), [Go](https://mrpc.pro/demo/mt5_demo.go), [Java](https://mrpc.pro/demo/Mt5Demo.java),
[C++](https://mrpc.pro/demo/mt5_demo.cpp), [Rust](https://mrpc.pro/demo/mt5_demo.rs), [Ruby](https://mrpc.pro/demo/mt5_demo.rb).

Other platforms have their own guides: [cTrader](https://mrpc.pro/docs-legacy/ct-client-api),
[TradeLocker](https://mrpc.pro/docs-legacy/tl-client-api), [Trade Copier](https://mrpc.pro/docs-legacy/trade-copier).
Their error format differs from MT4 / MT5.

Limits, streams and timeouts are also stated in section 6 of the Terms of Service: <https://mrpc.pro/terms>.

## 12. Pricing (summary; the calculator is at <https://mrpc.pro/#pricing>)

- Creating an account is free. Usage is paid from a prepaid balance; any welcome credit is shown in your dashboard.
- Each connected terminal is charged **per minute at an hourly rate**, while it is connected. A standard MT4 / MT5 terminal
  costs $4 per 720 hours (one month of continuous running). Volume rates for the month-to-date usage of all your terminals
  lower this step by step to $1 per 720 hours after 1,500,000 combined account-hours in a calendar month.
- cTrader and TradeLocker connections cost half of the MetaTrader price.
- A trade copier costs $1 per 720 hours while it runs, billed by the minute.
- At zero balance, connections are suspended. They resume by themselves after a deposit.
- Each terminal is billed separately, including a second terminal on the same account (section 10). Disconnect terminals
  you no longer need.

## 13. Checklist for an integration

1. Get an API key (<https://mrpc.pro/signup>) and send it as `APIKey` on every call.
2. `ConnectEx` and keep `terminalInstanceGuid`. Send it as `id` on every later call.
3. Check `error` in every reply before you use `data`. Branch on `errorCode`.
4. Read prices, positions and trade events from streams, not by polling. Stay under 300 calls a minute per terminal
   (orders do not count).
5. On `TERMINAL_RATE_LIMIT_EXCEEDED` / `TERMINAL_BUSY`, wait `RetryAfterSeconds`. After a timeout on an order, reconcile by
   `expert_id` before resending.
6. `Disconnect` in a `finally` block when done.

Support: Telegram [@mrpcio](https://t.me/mrpcio) · admin@mrpc.pro
