Skip to main content

Connection

wss://api.easierprop.com/ws?apiKey=sk_your_key

Subscribe to a Channel

Send a JSON message to start streaming:
{
  "type": "subscribe",
  "channel": "quotes",
  "account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3",
  "symbols": ["EURUSD", "GBPUSD"],
  "interval_ms": 500
}
Server confirms:
{
  "type": "subscribed",
  "channel": "quotes",
  "account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3",
  "symbols": ["EURUSD", "GBPUSD"]
}

Receive Events

Quote Ticks

Quote data is wrapped in a data field containing the upstream quote object:
{
  "type": "quote",
  "account_id": "70f60784-...",
  "data": {
    "ask": 1.08544,
    "bid": 1.08542,
    "last": 0.0,
    "symbol": "EURUSD",
    "time": "2026-04-01T12:30:00",
    "volume": 0.0
  }
}

Order Updates

Sent when an order is opened, modified, or closed. Order data is wrapped in a data field:
{
  "type": "order_update",
  "account_id": "70f60784-...",
  "data": {
    "ticket": 12345678,
    "symbol": "EURUSD",
    "orderType": "Buy",
    "lots": 0.01,
    "openPrice": 1.08542,
    "profit": 3.20,
    "swap": 0.0,
    "commission": -0.07
  }
}

Profit Updates

Live equity and P&L, wrapped in a data field:
{
  "type": "profit",
  "account_id": "70f60784-...",
  "data": {
    "balance": 10000.00,
    "equity": 10142.50,
    "profit": 142.50,
    "margin": 500.00,
    "freeMargin": 9642.50
  }
}

Heartbeat

Periodic account health snapshot with balance, equity, margin, and position count:
{
  "type": "heartbeat",
  "account_id": "70f60784-...",
  "balance": 10000.00,
  "equity": 10142.50,
  "free_margin": 9642.50,
  "margin": 500.00,
  "position_count": 3,
  "profit": 142.50
}
Subscribe to heartbeat:
{
  "type": "subscribe",
  "channel": "heartbeat",
  "account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3"
}

New Position

Fires when a new position is opened on the account. The full order object is delivered in the data field:
{
  "type": "new_position",
  "account_id": "70f60784-...",
  "data": {
    "ticket": 12345680,
    "symbol": "GBPUSD",
    "orderType": "Buy",
    "lots": 0.02,
    "openPrice": 1.26110,
    "profit": 0.0
  }
}
Subscribe to new positions:
{
  "type": "subscribe",
  "channel": "new_position",
  "account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3"
}

Alerts

Register price, spread, or P&L alerts to be notified when conditions are met. Alerts are one-shot: they fire once and are automatically removed.

Price Alert

Register a price alert to trigger when a symbol’s price crosses a threshold:
{
  "type": "price_alert",
  "account_id": "70f60784-...",
  "symbol": "EURUSD",
  "direction": "above",
  "target_price": 1.09000
}
Server confirms registration:
{
  "type": "alert_registered",
  "alert_id": "a1b2c3d4-...",
  "alert_type": "price_alert"
}
When triggered:
{
  "type": "price_alert_triggered",
  "account_id": "70f60784-...",
  "alert_id": "a1b2c3d4-...",
  "current_ask": 1.09010,
  "current_bid": 1.09008,
  "direction": "above",
  "symbol": "EURUSD",
  "target_price": 1.09000
}

Spread Alert

Register a spread alert to trigger when the spread exceeds a threshold in pips:
{
  "type": "spread_alert",
  "account_id": "70f60784-...",
  "symbol": "EURUSD",
  "max_pips": 2.0
}
When triggered:
{
  "type": "spread_alert_triggered",
  "account_id": "70f60784-...",
  "alert_id": "e5f6g7h8-...",
  "ask": 1.08560,
  "bid": 1.08530,
  "max_pips": 2.0,
  "spread_pips": 3.0,
  "symbol": "EURUSD"
}

P&L Alert

Register a P&L alert to trigger when account profit crosses a threshold:
{
  "type": "pl_alert",
  "account_id": "70f60784-...",
  "condition": "below",
  "threshold": -500.00
}
When triggered:
{
  "type": "pl_alert_triggered",
  "account_id": "70f60784-...",
  "alert_id": "i9j0k1l2-...",
  "condition": "below",
  "current_value": -520.00,
  "threshold": -500.00
}

Cancel Alert

Cancel a previously registered alert before it triggers:
{
  "type": "cancel_alert",
  "alert_id": "a1b2c3d4-..."
}

Aggregate Feed

The aggregate channel combines quotes, orders, and profit updates into a single subscription. Useful when you want all event types without multiple subscribe calls:
{
  "type": "subscribe",
  "channel": "aggregate",
  "account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3",
  "symbols": ["EURUSD", "GBPUSD"]
}
You will receive quote, order_update, and profit messages on this single subscription, each with the standard data wrapper.

Unsubscribe

{
  "type": "unsubscribe",
  "channel": "quotes",
  "account_id": "70f60784-...",
  "symbols": ["EURUSD"]
}
Unsubscribe from everything on an account:
{
  "type": "unsubscribe_all",
  "account_id": "70f60784-..."
}

Available Channels

ChannelDescriptionsymbols requiredinterval_ms default
quotesReal-time bid/ask ticksyes500ms
ordersOrder open/modify/close eventsno
profitLive equity and P&Lno1000ms
heartbeatAccount health snapshotno5000ms
new_positionNew position openedno
aggregateCombined quotes + orders + profityes500ms

Multi-Account Streaming

Subscribe to multiple accounts on the same WebSocket:
// Stream quotes from account A
ws.send(JSON.stringify({
  type: "subscribe",
  channel: "quotes",
  account_id: "account-a-uuid",
  symbols: ["EURUSD"]
}));

// Stream profit from account B
ws.send(JSON.stringify({
  type: "subscribe",
  channel: "profit",
  account_id: "account-b-uuid",
  interval_ms: 1000
}));

// Stream order fills from both
ws.send(JSON.stringify({
  type: "subscribe",
  channel: "orders",
  account_id: "account-a-uuid"
}));

ws.send(JSON.stringify({
  type: "subscribe",
  channel: "orders",
  account_id: "account-b-uuid"
}));
Every message includes account_id so you can route it to the correct handler.

Error Events

{
  "type": "error",
  "code": "SESSION_ERROR",
  "message": "No active session for this account"
}
Error codes: SESSION_ERROR, INVALID_MESSAGE

Keepalive

{ "type": "ping" }
{ "type": "pong" }

Full JavaScript Example

const ws = new WebSocket("wss://api.easierprop.com/ws?apiKey=sk_your_key");

ws.onopen = () => {
  console.log("Connected to Easier Prop WebSocket");

  // Stream live quotes
  ws.send(JSON.stringify({
    type: "subscribe",
    channel: "quotes",
    account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3",
    symbols: ["EURUSD", "GBPUSD", "USDJPY"],
    interval_ms: 500
  }));

  // Monitor order fills
  ws.send(JSON.stringify({
    type: "subscribe",
    channel: "orders",
    account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3"
  }));

  // Track live P&L
  ws.send(JSON.stringify({
    type: "subscribe",
    channel: "profit",
    account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3",
    interval_ms: 1000
  }));

  // Heartbeat for account health
  ws.send(JSON.stringify({
    type: "subscribe",
    channel: "heartbeat",
    account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3"
  }));

  // Price alert
  ws.send(JSON.stringify({
    type: "price_alert",
    account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3",
    symbol: "EURUSD",
    direction: "above",
    target_price: 1.09000
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case "subscribed":
      console.log(`Subscribed to ${msg.channel}`);
      break;

    case "quote":
      console.log(`${msg.data.symbol}: ${msg.data.bid} / ${msg.data.ask}`);
      break;

    case "order_update":
      console.log(`Order ${msg.data.ticket}: ${msg.data.orderType} ${msg.data.symbol} P&L=${msg.data.profit}`);
      break;

    case "profit":
      console.log(`Equity: ${msg.data.equity} | Profit: ${msg.data.profit}`);
      break;

    case "heartbeat":
      console.log(`Heartbeat: balance=${msg.balance} equity=${msg.equity} positions=${msg.position_count}`);
      break;

    case "new_position":
      console.log(`New position: ${msg.data.ticket} ${msg.data.orderType} ${msg.data.symbol}`);
      break;

    case "alert_registered":
      console.log(`Alert registered: ${msg.alert_id} (${msg.alert_type})`);
      break;

    case "price_alert_triggered":
      console.log(`Price alert! ${msg.symbol} crossed ${msg.target_price} (${msg.direction})`);
      break;

    case "spread_alert_triggered":
      console.log(`Spread alert! ${msg.symbol} spread=${msg.spread_pips} pips`);
      break;

    case "pl_alert_triggered":
      console.log(`P&L alert! ${msg.condition} ${msg.threshold}, current=${msg.current_value}`);
      break;

    case "error":
      console.error(`Error: ${msg.message}`);
      break;
  }
};

ws.onclose = () => console.log("Disconnected");

Full Python Example

import asyncio
import json
import websockets

API_KEY = "sk_your_key"
ACCOUNT_ID = "70f60784-20f1-45ba-9a04-8e01c0b810c3"

async def main():
    uri = f"wss://api.easierprop.com/ws?apiKey={API_KEY}"

    async with websockets.connect(uri) as ws:
        # Subscribe to quotes
        await ws.send(json.dumps({
            "type": "subscribe",
            "channel": "quotes",
            "account_id": ACCOUNT_ID,
            "symbols": ["EURUSD"],
            "interval_ms": 500
        }))

        # Subscribe to P&L
        await ws.send(json.dumps({
            "type": "subscribe",
            "channel": "profit",
            "account_id": ACCOUNT_ID,
            "interval_ms": 1000
        }))

        # Subscribe to heartbeat
        await ws.send(json.dumps({
            "type": "subscribe",
            "channel": "heartbeat",
            "account_id": ACCOUNT_ID
        }))

        # Register a price alert
        await ws.send(json.dumps({
            "type": "price_alert",
            "account_id": ACCOUNT_ID,
            "symbol": "EURUSD",
            "direction": "above",
            "target_price": 1.09000
        }))

        async for message in ws:
            msg = json.loads(message)

            if msg["type"] == "quote":
                d = msg["data"]
                print(f"{d['symbol']}: {d['bid']} / {d['ask']}")
            elif msg["type"] == "profit":
                d = msg["data"]
                print(f"Equity: {d['equity']} | P&L: {d['profit']}")
            elif msg["type"] == "heartbeat":
                print(f"Heartbeat: balance={msg['balance']} positions={msg['position_count']}")
            elif msg["type"] == "price_alert_triggered":
                print(f"Price alert! {msg['symbol']} crossed {msg['target_price']}")
            elif msg["type"] == "error":
                print(f"Error: {msg['message']}")

asyncio.run(main())

Session Behavior

  • Sessions auto-connect when you subscribe. No need to call /connect first.
  • If the MT5 session drops, the gateway reconnects automatically and resumes streaming.
  • On WebSocket disconnect, all subscriptions for that client are cleaned up.
  • Upstream quote subscriptions are ref-counted. Multiple clients on the same symbol share one upstream feed.