How to Stream Real-Time Rates via WebSocket using Currencies API

This tutorial shows how to connect to FinFeedAPI’s Currencies WebSocket, authenticate with an API key, subscribe to one or more currency pairs, and consume live exrate messages (real-time exchange rates).

By the end, you’ll have a working WebSocket client that:

  • Connects to the production endpoint
  • Authenticates using an API key
  • Subscribes to specific asset pairs (e.g., BTC/EUR, ETH/CNY)
  • Prints incoming exchange-rate updates (type: "exrate")
  • Handles server messages like heartbeat, reconnect, and error
  • A FinFeedAPI API key (from the FinFeed/API Bricks console)
  • A WebSocket client:
    • Quick test: wscat (Node)
    • App code: Python

Use:

1wss://api-realtime.fx.finfeedapi.com

Your client must respond with WebSocket Pong frames when the server sends Ping (about every minute), or the server may close the connection.

Exchange-rate updates arrive like:

1{
2  "type":"exrate",
3  "asset_id_base":"BTC",
4  "asset_id_quote":"USD",
5  "rate_type":"BASE_ALL_CROSSES_TO_REF",
6  "time":"2019-06-11T15:26:00.0000000Z",
7  "rate":10065.0319541
8}

FinFeedAPI describes this exchange rate as a rolling VWAP-24H (volume-weighted average price across selected data sources).

FinFeedAPI supports multiple API-key authentication methods (including for WebSocket). The two most practical for WebSocket clients are:

Connect to:

1wss://api-realtime.fx.finfeedapi.com?apikey=YOUR_API_KEY

Send:

1X-APIBricks-Key: YOUR_API_KEY
Some environments don’t let you set arbitrary WebSocket headers. In those cases, use the query string approach.

After connecting, send a hello message to define your subscription scope.

Example:

1{
2  "type":"hello",
3  "heartbeat":false,
4  "subscribe_filter_asset_id": ["BTC/EUR"]
5}
  • type: hello (initializes a fresh subscription scope)
  • heartbeat: if true, you’ll receive a heartbeat message when there’s been 1 second of silence
  • subscribe_filter_asset_id: list of assets or asset pairs (e.g., BTC/EUR)

Instead of sending another hello (which overrides scope), you can dynamically adjust with:

1{
2  "type":"subscribe",
3  "heartbeat":true,
4  "subscribe_filter_asset_id": ["ETH/CNY"]
5}
1{
2  "type":"unsubscribe",
3  "heartbeat":true,
4  "subscribe_filter_asset_id": ["BTC/EUR"]
5}

FinFeedAPI’s doc summary:

  • subscribe expands scope
  • unsubscribe removes items
  • hello replaces the scope

FinFeedAPI recommends wscat for debugging.

1npm i-g wscat
1wscat-c"wss://api-realtime.fx.finfeedapi.com?apikey=YOUR_API_KEY
1{"type":"hello","heartbeat":true,"subscribe_filter_asset_id":["BTC/EUR","ETH/CNY"],"subscribe_update_limit_ms_exrate":1000}

If auth + payload are valid, you should start seeing exrate updates and/or heartbeat messages.

Install:

1pip install websockets

Example client (prints rates, handles basic message types):

1importasyncio
2importjson
3importwebsockets
4
5API_KEY="YOUR_API_KEY"
6WS_URL=f"wss://api-realtime.fx.finfeedapi.com?apikey={API_KEY}"
7
8HELLO= {
9"type":"hello",
10"heartbeat":True,
11"subscribe_filter_asset_id": ["BTC/EUR","ETH/CNY"],
12"subscribe_update_limit_ms_exrate":1000
13}
14
15asyncdefmain():
16asyncwithwebsockets.connect(WS_URL,ping_interval=None)asws:
17# Send subscription scope
18awaitws.send(json.dumps(HELLO))
19
20whileTrue:
21raw=awaitws.recv()
22msg=json.loads(raw)
23
24t=msg.get("type")
25ift=="exrate":
26print(f'{msg["time"]}{msg["asset_id_base"]}/{msg["asset_id_quote"]} '
27f'({msg.get("rate_type")}):{msg["rate"]}')
28elift=="heartbeat":
29# Useful for detecting a dead connection vs “no market updates”
30print("heartbeat")
31elift=="reconnect":
32# Server asks you to reconnect within a time window
33print(f"reconnect requested:{msg}")
34# A simple approach is to break and let your outer loop reconnect.
35break
36elift=="error":
37# Server will close connection after sending an error
38raiseRuntimeError(msg.get("message","Unknown error"))
39else:
40print(f"other message:{msg}")
41
42if__name__=="__main__":
43asyncio.run(main())
44

Notes:

  • The server sends Ping frames; most WebSocket libraries respond with Pong automatically, but don’t disable that behavior.
  • If you receive an error message, FinFeedAPI treats it as permanent and expects the connection to be closed afterward
  • You receive { "type": "error", "message": "Invalid API key" }
    • Confirm you’re passing the key via query string or supported auth header format.
  • You get disconnected under heavy load / broad subscriptions
    • FinFeedAPI may disconnect if your client can’t read fast enough and the server’s buffer fills (“Reached maximum allowed buffered messages…”). Keep your receive loop lightweight and offload processing elsewhere.
  • Metadata tables provide downloadable asset lists (and the API can list assets via GET /v1/assets)

You’ve now got a working real-time currency-rate stream over WebSocket: connected to the production endpoint, authenticated with an API key, subscribed to asset pairs, and consuming exrate updates (plus heartbeat / reconnect / error handling).