> For the complete documentation index, see [llms.txt](https://www.bluegamma.io/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.bluegamma.io/documentation/integrations/api/how-to-guides/streaming-real-time-fx-data.md).

# Streaming Real-Time FX Data

Stream real-time FX quotes via WebSocket for live mid, bid, and ask prices across roughly 945 currency pairs.

The BlueGamma FX streaming API provides real-time foreign exchange quotes via WebSocket. Use this to power live dashboards, monitor currency exposure, or feed real-time FX rates into pricing engines.

***

## WebSocket Endpoint

```
wss://streaming.bluegamma.io/fx_stream
```

***

## Quick Start

Connect to the stream with your API token and receive real-time quotes:

```python
import asyncio
import json
import websockets

async def stream_fx():
    url = 'wss://streaming.bluegamma.io/fx_stream?s=EURUSD&s=GBPUSD&token=YOUR_API_KEY'
    
    async with websockets.connect(url) as ws:
        async for msg in ws:
            data = json.loads(msg)
            if data.get("type") == "quote":
                print(f"{data['symbol']}: mid={data['mid']}, bid={data['bid']}, ask={data['ask']}")

asyncio.run(stream_fx())
```

```bash
# Using wscat (install with: npm install -g wscat)
wscat -c "wss://streaming.bluegamma.io/fx_stream?s=EURUSD&s=GBPUSD&token=YOUR_API_KEY"
```

**Sample output:**

```
EURUSD: mid=1.16125, bid=1.1612, ask=1.1613
GBPUSD: mid=1.3396, bid=1.3395, ask=1.3397
USDJPY: mid=158.1329, bid=158.125, ask=158.1408
```

***

## Query Parameters

| Parameter | Required | Description                                                                                                                                                                                 |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `s`       | No       | Symbol(s) to subscribe to. Repeat for multiple symbols (e.g., `?s=EURUSD&s=GBPUSD`)                                                                                                         |
| `token`   | Yes      | Your BlueGamma API key. See [Authentication](/documentation/integrations/api/authentication.md). You can send the key in an `x-api-key` header instead, which keeps it out of URLs and logs |

For the full list of supported currency pairs, see [Supported FX Currency Pairs](/documentation/data-coverage/data-coverage/supported-fx-currency-pairs.md).

***

## Server Messages

The server sends JSON messages to your client. Each message has a `type` field indicating its purpose.

### Connection Confirmation

Sent immediately after connecting:

```json
{
  "type": "connected",
  "client_id": "172.31.33.250_1768555125108",
  "subscribed": ["GBPUSD", "USDJPY", "EURUSD"]
}
```

| Field        | Description                                    |
| ------------ | ---------------------------------------------- |
| `client_id`  | Unique identifier for your session             |
| `subscribed` | List of symbols you're currently subscribed to |

### Real-Time Quote

Sent continuously as prices update:

```json
{
  "type": "quote",
  "symbol": "EURUSD",
  "mid": 1.16125,
  "bid": 1.1612,
  "ask": 1.1613,
  "timestamp": "2026-01-16T09:18:46.749Z"
}
```

| Field       | Description                              |
| ----------- | ---------------------------------------- |
| `symbol`    | Currency pair                            |
| `mid`       | Mid-market rate (average of bid and ask) |
| `bid`       | Best bid price                           |
| `ask`       | Best ask price                           |
| `timestamp` | ISO 8601 timestamp in UTC                |

### Subscription Confirmation

Sent when you subscribe to new symbols:

```json
{
  "type": "subscribed",
  "symbols": ["AUDUSD"]
}
```

### Unsubscription Confirmation

Sent when you unsubscribe from symbols:

```json
{
  "type": "unsubscribed",
  "symbols": ["GBPUSD"]
}
```

### Heartbeat

The server sends a ping about every 30 seconds to keep the connection alive. If you receive neither a ping nor a quote for 60 seconds, reconnect:

```json
{"type": "ping"}
```

If you send a ping, you'll receive:

```json
{"type": "pong"}
```

### Error

Sent when something goes wrong:

```json
{
  "type": "error",
  "error": "Subscription limit of 50 symbols per connection would be exceeded",
  "rejected_symbols": ["AUDUSD", "NZDUSD"],
  "current_count": 49,
  "requested_count": 2
}
```

A subscribe request that would take the connection over 50 symbols is rejected whole. An unknown or malformed symbol gets an error that names it.

***

## Client Commands

Send JSON messages to the server to manage your subscriptions.

### Subscribe to Additional Symbols

```json
{"action": "subscribe", "symbols": ["AUDUSD", "NZDUSD"]}
```

**Response:**

```json
{"type": "subscribed", "symbols": ["AUDUSD", "NZDUSD"]}
```

### Unsubscribe from Symbols

```json
{"action": "unsubscribe", "symbols": ["GBPUSD"]}
```

**Response:**

```json
{"type": "unsubscribed", "symbols": ["GBPUSD"]}
```

### Ping

```json
{"action": "ping"}
```

**Response:**

```json
{"type": "pong"}
```

***

## Supported Currency Pairs

The stream supports roughly **945 currency pairs**, the same set as the `/fx` spot endpoint, including:

* **Major pairs**: EURUSD, GBPUSD, USDJPY, USDCHF, AUDUSD, USDCAD, NZDUSD
* **Euro crosses**: EURGBP, EURJPY, EURCHF, EURAUD, EURCAD, and more
* **Other crosses**: GBPJPY, CHFJPY, AUDJPY, CADJPY, and more
* **Emerging markets**: USDMXN, USDBRL, USDZAR, USDTRY, USDCNY, and many more

{% hint style="info" %}
For the complete list, including a downloadable CSV, see [Supported FX Currency Pairs](/documentation/data-coverage/data-coverage/supported-fx-currency-pairs.md).
{% endhint %}

***

## Use Cases

| Use Case                  | Description                                           |
| ------------------------- | ----------------------------------------------------- |
| **Real-time dashboards**  | Power live FX rate displays and monitoring screens    |
| **Risk monitoring**       | Track currency exposure in real-time                  |
| **Pricing engines**       | Real-time FX component for multi-asset pricing        |
| **Cash flow forecasting** | Monitor FX rates affecting foreign currency positions |

***

## Complete Example with Reconnection

Connections drop: networks change, proxies close idle sockets, and servers restart during deploys. For production use, reconnect automatically:

* Track when you last received **any** message. Quotes and pings both count, so this works in quiet periods too (FX quotes stop at weekends).
* If nothing arrives for 60 seconds, close the connection and open a new one.
* Reconnect with exponential backoff (1s, 2s, 4s and so on, capped at 30s) plus random jitter.
* **Subscribe again after reconnecting.** Subscriptions belong to a connection and are not carried over, and the stream restarts from the next live quote (missed quotes are not replayed).

```python
import asyncio
import json
import random
import websockets

API_KEY = "your_api_key"
SYMBOLS = ["EURUSD", "GBPUSD", "USDJPY"]
URL = f"wss://streaming.bluegamma.io/fx_stream?token={API_KEY}"
SILENCE_LIMIT_S = 60  # no quote and no ping for 60s: reconnect

def handle(data):
    msg_type = data.get("type")
    if msg_type == "quote":
        print(f"[{data['timestamp']}] {data['symbol']}: mid={data['mid']:.5f}, bid={data['bid']:.5f}, ask={data['ask']:.5f}")
    elif msg_type == "subscribed":
        print(f"Subscribed to: {data['symbols']}")
    elif msg_type == "error":
        print(f"Error: {data['error']}")

async def run_with_reconnection():
    attempt = 0
    while True:
        try:
            async with websockets.connect(URL) as ws:
                attempt = 0
                # Subscriptions belong to the connection: subscribe on every connect
                await ws.send(json.dumps({"action": "subscribe", "symbols": SYMBOLS}))
                while True:
                    # Any message (quote or ping) resets the clock;
                    # 60s of silence raises TimeoutError and triggers a reconnect
                    message = await asyncio.wait_for(ws.recv(), timeout=SILENCE_LIMIT_S)
                    handle(json.loads(message))
        except Exception as e:
            # Exponential backoff with jitter, capped at 30 seconds
            delay = min(30, 2 ** attempt) * (0.5 + random.random())
            attempt += 1
            print(f"Disconnected ({e}); reconnecting in {delay:.1f}s")
            await asyncio.sleep(delay)

if __name__ == "__main__":
    asyncio.run(run_with_reconnection())
```

***

## Limits and Best Practices

| Limit                       | Value                                                                     |
| --------------------------- | ------------------------------------------------------------------------- |
| Max symbols per connection  | 50                                                                        |
| Max connections per API key | 25                                                                        |
| Typical update frequency    | \~300ms for major pairs (e.g. EURUSD, GBPUSD), \~1s for less liquid pairs |

**Best practices:**

1. **Implement reconnection logic**: reconnect automatically with exponential backoff, and subscribe again after each reconnect
2. **Watch for silence**: the server sends a ping about every 30 seconds, so 60 seconds with no ping and no quote means the connection is dead
3. **Subscribe dynamically** — Use subscribe/unsubscribe commands rather than reconnecting to change symbols
4. **Process messages asynchronously** — Don't block the message loop with slow operations

***

## Error Handling

| Error                                                               | Cause                                                                                           | Solution                                                      |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Connection refused with HTTP 403                                    | Invalid or missing API key                                                                      | Check your `token` parameter or `x-api-key` header            |
| Connection refused with HTTP 503 and a `Retry-After` header         | 25 connections already open for your key, the server is at capacity, or a deploy is in progress | Wait the `Retry-After` seconds, then reconnect                |
| "Subscription limit of 50 symbols per connection would be exceeded" | The request would take the connection over 50 symbols                                           | Subscribe to fewer symbols, or spread them across connections |
| Connection closed with code 1013                                    | Your client is not reading messages fast enough                                                 | Process messages asynchronously, then reconnect               |
| Connection closed with code 1001                                    | The server is restarting during a deploy                                                        | Reconnect and subscribe again                                 |
| No message for 60 seconds                                           | Network issue or a dropped connection                                                           | Reconnect with exponential backoff                            |

***

## Related

* [Authentication](/documentation/integrations/api/authentication.md) — How to get and use your API key
* [Fetching FX Forward Rates](/documentation/integrations/api/how-to-guides/fetching-fx-forward-rates.md) — REST API for FX forward rates
* [Rate Limits & Best Practices](/documentation/integrations/api/rate-limits.md) — General API best practices

***

Create a key under **API Keys** in the app sidebar. See [Authentication](/documentation/integrations/api/authentication.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://www.bluegamma.io/documentation/integrations/api/how-to-guides/streaming-real-time-fx-data.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
