Stream MT5 quotes in Python with server-sent events
Read MT5 bid and ask updates in Python using authenticated SSE. Handle stream closure, refresh snapshots and keep your API key out of the URL.
Use an authenticated HTTP stream
MetaBridgeAPI exposes MT5 quote updates through server-sent events, or SSE. Your client opens an HTTPS GET request, authenticates with a Bearer header and reads events from the response until it closes. A quote event contains the broker symbol, bid, ask and UTC time. This example uses Python’s standard library and sends no trading requests.
Streaming suits an application that needs repeated updates without issuing a new quote request each time. Treat the stream as sampled updates, not a complete archive of every market tick. A successful HTTP connection confirms that the stream opened; receiving a quote is a separate step. The MT5 API overview explains the other available account and market-data features.
Connect the account and set your environment
Connect your MT5 account in the console and activate access for it. The 14-day trial covers one broker-confirmed demo account; paid access covers selected demo or live accounts. In Explore data, read a quote for the exact symbol your broker provides. Include any broker suffix in the symbol name.
Create an API key with market:read for snapshot requests and streams:read for the stream. Restrict the key to the selected account. Provide these environment variables to the Python process through your normal secret and configuration management:
- METABRIDGE_API_KEY: the complete key copied from the console.
- METABRIDGE_WORKSPACE_ID: the workspace UUID.
- METABRIDGE_ACCOUNT_ID: the connected MT5 account UUID, rather than its broker login.
- METABRIDGE_SYMBOL: one exact broker symbol for this example.
Quote streams accept one to ten distinct symbols as a comma-separated query value. Start with one symbol so the output is easy to compare with Explore data. Keep the key in the Authorization header. Putting it in a URL risks exposing it through browser history and request logs.
Read a snapshot, then process quote events
Save the following as stream_quotes.py and run it with Python 3 after supplying the environment variables. It reads an initial snapshot, prints quote events, handles the server’s close event and attempts another snapshot when the stream finishes. It intentionally makes one stream connection so you can see how it ends before adding your own reconnect policy.
import json
import os
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
base = "https://metabridgeapi.com/api/v1"
workspace = os.environ["METABRIDGE_WORKSPACE_ID"]
account = os.environ["METABRIDGE_ACCOUNT_ID"]
symbol = os.environ["METABRIDGE_SYMBOL"]
account_url = f"{base}/organizations/{workspace}/accounts/{account}"
authorization = "Bearer " + os.environ["METABRIDGE_API_KEY"]
def request(path, accept="application/json"):
return Request(account_url + path, headers={
"Authorization": authorization,
"Accept": accept,
})
def snapshot():
path = "/quote?" + urlencode({"symbol": symbol})
with urlopen(request(path), timeout=30) as response:
print("snapshot", json.load(response)["data"])
def stream_quotes():
path = "/quotes/stream?" + urlencode({"symbols": symbol})
with urlopen(request(path, "text/event-stream"), timeout=60) as response:
event, data = "message", []
for raw in response:
line = raw.decode("utf-8").rstrip("\r\n")
if line == "":
if data:
payload = json.loads("\n".join(data))
if event == "close":
print("stream closed", payload)
break
if event == "quote":
print("quote", payload)
event, data = "message", []
elif line.startswith(":"):
continue # SSE comments and heartbeats are not quotes.
elif line.startswith("event:"):
event = line[6:].lstrip(" ")
elif line.startswith("data:"):
data.append(line[5:].lstrip(" "))
try:
snapshot()
try:
stream_quotes()
finally:
snapshot()
except HTTPError as error:
# Inspect the error body/request ID; never print request headers.
print("HTTP error", error.code, error.read().decode("utf-8"))
except (URLError, TimeoutError) as error:
print("Connection ended", type(error).__name__)
except KeyboardInterrupt:
print("Stopped")SSE separates events with a blank line. The parser collects data lines until that boundary and ignores comments, including heartbeats. Do not interpret a heartbeat as a fresh price. In your application, store each quote’s timestamp alongside its values and decide how to display stale or unavailable data.
Reconnect explicitly and rebuild current state
A stream lasts up to ten minutes and may close earlier. Your client must explicitly reconnect. There is no replay guarantee, so do not assume an event ID or the Last-Event-ID header will recover missed updates. Read a fresh snapshot when reconnecting and allow subsequent stream events to update it. This gives you current state, rather than a reconstruction of every event during the gap.
Inspect HTTP failures before retrying. A revoked key, missing permission or expired account access needs correction; rapid reconnect attempts will not fix it. For transient failures, use a bounded delay and backoff, and respect Retry-After when the API returns 429. Stop the reconnect loop when your application shuts down or no longer needs the subscription.
Check behavior before wiring up your application
- Compare the initial snapshot with a quote in Explore data for the same exact symbol.
- Confirm that quote events include the expected symbol and a timestamp you can interpret.
- Stop the script and confirm your application handles the absence of updates explicitly.
- Exercise a reconnect and refresh the snapshot instead of assuming the old value is still current.
The API guide also describes order and profit streams. They use the same Bearer authentication and explicit reconnect approach, but their payloads represent positions and account state. An empty position list can be a valid response. Keep market-data processing separate from any logic that submits a trade, and inspect trading operation outcomes independently.