API documentation
QUOTC streams independently generated OTC quotes for 58 currency pairs over a WebSocket connection. Your server connects once, authenticates with an API key and receives a price for every available pair each second, 24/7.
Quick start
- Create an account and open My keys in your dashboard.
- Create a test key. Enter the public IPv4 or IPv6 address of the server that will connect to the feed: the key works only from that address.
- Copy the full key with the Copy button and store it on your server, for example in an environment variable. Test keys start with
qtc_, production keys withprd_. - From that server, open a WebSocket connection to the endpoint below and send the authentication message.
- Receive a tick for every available pair each second. Reconnect automatically if the connection drops.
API keys
| Test key | Production key | |
|---|---|---|
| Currency pairs | EUR/USD, USD/JPY, AUD/USD, USD/CAD, USD/CHF, NZD/USD, EUR/GBP, EUR/JPY, GBP/JPY, AUD/JPY | All 58 pairs |
| Price | Free | 500 USD / month per key |
| Validity | 14 days from creation, one per account | Set by our team on activation |
| Key prefix | qtc_ |
prd_ |
Create a production key in your dashboard, then open a support ticket to arrange payment. A new production key stays inactive until our team activates it and sets its expiry date. You can create several production keys, for example one per server.
Every key is bound to one server IP address. Connections from any other address are rejected. To change the address or renew a key, contact support from your dashboard.
Keep the key secret. Use it only on your server: never place it in browser code, mobile apps or a public repository. If a key leaks, contact support to replace it.
Connection
Endpoint
wss://wss.quotc.net
Use any WebSocket client for your language. Within 10 seconds of opening the connection, send the authentication message with your key:
{"type": "auth", "token": "qtc_YOUR_KEY", "protocol": 2}Set protocol to 2 to receive service messages (auth_ok, subscribed). Clients that omit it receive only ticks and pong messages.
After successful authentication the server replies with auth_ok. It lists the pairs available to the key and the key expiry time in Unix seconds (UTC):
{
"type": "auth_ok",
"expiresAt": 1795683600,
"symbols": [
{ "symbol": "EURUSD", "label": "EUR/USD", "digits": 5 }
]
}A key allows up to 10 simultaneous connections, so several services on the same server can share it.
Closing and reconnecting
Use the close code to decide whether to reconnect:
| Code | Meaning | What to do |
|---|---|---|
1008 |
Access denied: unknown key, connection from another IP, key inactive, expired or deleted, too many connections, or no authentication within 10 seconds. | Do not reconnect with the same key. Check the key status and IP address in your dashboard. |
1011 |
Key verification is temporarily unavailable. | Reconnect after 3–5 seconds and authenticate again. |
| Other | Network error or service restart. | Reconnect after 3–5 seconds and authenticate again. |
Open connections are checked again every minute. If a key is deactivated, expires, is deleted or replaced, its connections are closed with code 1008.
Quotes
Every second the server sends one message for each pair available to your key:
{
"symbol": "EURUSD",
"value": 1.11609,
"open": 1.11608,
"high": 1.11609,
"low": 1.11608,
"volume": 2,
"ts": 1793091620000
}| Field | Description |
|---|---|
symbol |
Pair symbol, for example EURUSD. See the pair list below. |
value |
Current price: the close of the last second. |
open, high, low |
Open, high and low price of the last second. |
volume |
Relative activity of the last second. It is comparable between pairs and is not a traded volume. |
ts |
Tick time in Unix milliseconds (UTC). |
Prices are rounded to the number of decimal places shown for each pair in the list below.
Requests
After authentication your client can send the following messages. Responses are shown after the arrow.
Ping
Keeps the connection alive through proxies. Send it every 30 seconds; the server replies with pong and the same timestamp.
{"type": "ping", "timestamp": 1793091620000}
→ {"type": "pong", "timestamp": 1793091620000}Select pairs
By default you receive all pairs available to the key. Send subscribe to limit the stream to the listed pairs; pairs not available to the key are ignored. Send it again at any time to change the list.
{"type": "subscribe", "symbols": ["EURUSD", "USDJPY"]}
→ {"type": "subscribed", "symbols": ["EURUSD", "USDJPY"]}Code examples
Complete clients that authenticate, print ticks and reconnect after network errors. Before running them, put your key in the QUOTC_API_KEY environment variable on the server whose IP is bound to the key.
// npm install ws
const WebSocket = require('ws');
const URL = 'wss://wss.quotc.net';
const API_KEY = process.env.QUOTC_API_KEY; // keep the key out of source code
function connect() {
const ws = new WebSocket(URL);
let ping;
ws.on('open', () => {
ws.send(JSON.stringify({ type: 'auth', token: API_KEY, protocol: 2 }));
ping = setInterval(() => {
ws.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));
}, 30000);
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'auth_ok') {
console.log('Authorized. Pairs:', msg.symbols.map((s) => s.symbol).join(', '));
} else if (msg.symbol) {
console.log(msg.symbol, msg.value, new Date(msg.ts).toISOString());
}
});
ws.on('close', (code, reason) => {
clearInterval(ping);
if (code === 1008) {
console.error('Access denied:', reason.toString(), '- check the key and server IP');
return; // do not retry with the same key
}
setTimeout(connect, 3000); // network error or service restart
});
ws.on('error', (err) => console.error(err.message));
}
connect();
# pip install websockets
import asyncio
import json
import os
import websockets
URL = "wss://wss.quotc.net"
API_KEY = os.environ["QUOTC_API_KEY"] # keep the key out of source code
async def main():
while True:
try:
async with websockets.connect(URL) as ws:
await ws.send(json.dumps({"type": "auth", "token": API_KEY, "protocol": 2}))
async for raw in ws:
msg = json.loads(raw)
if msg.get("type") == "auth_ok":
print("Authorized. Pairs:", [s["symbol"] for s in msg["symbols"]])
elif "symbol" in msg:
print(msg["symbol"], msg["value"], msg["ts"])
except websockets.ConnectionClosed as e:
if e.rcvd is not None and e.rcvd.code == 1008:
print("Access denied:", e.rcvd.reason, "- check the key and server IP")
return # do not retry with the same key
except OSError as e:
print("Connection error:", e)
await asyncio.sleep(3) # network error or service restart
asyncio.run(main())
Connect from your server, not from the browser: the key is bound to the server IP and must stay secret. To show quotes on your website, relay them from your server to your visitors.
Available pairs
A production key covers all 58 pairs. The test key covers the pairs marked in the last column.
| Symbol | Pair | Decimal places | Test key |
|---|---|---|---|
AUDCAD |
AUD/CAD | 5 | — |
EURUSD |
EUR/USD | 5 | Yes |
AUDJPY |
AUD/JPY | 3 | Yes |
NZDUSD |
NZD/USD | 5 | Yes |
AUDUSD |
AUD/USD | 5 | Yes |
USDJPY |
USD/JPY | 3 | Yes |
USDCAD |
USD/CAD | 5 | Yes |
USDCHF |
USD/CHF | 5 | Yes |
EURGBP |
EUR/GBP | 5 | Yes |
EURJPY |
EUR/JPY | 3 | Yes |
EURCAD |
EUR/CAD | 5 | — |
GBPJPY |
GBP/JPY | 3 | Yes |
EURAUD |
EUR/AUD | 5 | — |
AUDCHF |
AUD/CHF | 5 | — |
CHFJPY |
CHF/JPY | 3 | — |
EURNZD |
EUR/NZD | 5 | — |
CADCHF |
CAD/CHF | 5 | — |
GBPCHF |
GBP/CHF | 5 | — |
CADJPY |
CAD/JPY | 3 | — |
GBPNZD |
GBP/NZD | 5 | — |
NZDCAD |
NZD/CAD | 5 | — |
NZDJPY |
NZD/JPY | 3 | — |
GBPCAD |
GBP/CAD | 5 | — |
NZDSGD |
NZD/SGD | 5 | — |
USDSGD |
USD/SGD | 5 | — |
USDSEK |
USD/SEK | 5 | — |
USDCZK |
USD/CZK | 3 | — |
EURHKD |
EUR/HKD | 5 | — |
GBPUSD |
GBP/USD | 5 | — |
EURCHF |
EUR/CHF | 5 | — |
GBPAUD |
GBP/AUD | 5 | — |
AUDNZD |
AUD/NZD | 5 | — |
NZDCHF |
NZD/CHF | 5 | — |
EURSEK |
EUR/SEK | 5 | — |
GBPSEK |
GBP/SEK | 5 | — |
EURCZK |
EUR/CZK | 3 | — |
USDHKD |
USD/HKD | 5 | — |
EURSGD |
EUR/SGD | 5 | — |
GBPSGD |
GBP/SGD | 5 | — |
AUDSGD |
AUD/SGD | 5 | — |
SGDJPY |
SGD/JPY | 3 | — |
USDNOK |
USD/NOK | 5 | — |
EURNOK |
EUR/NOK | 5 | — |
USDDKK |
USD/DKK | 5 | — |
EURDKK |
EUR/DKK | 5 | — |
USDPLN |
USD/PLN | 5 | — |
EURPLN |
EUR/PLN | 5 | — |
USDHUF |
USD/HUF | 3 | — |
EURHUF |
EUR/HUF | 3 | — |
USDTRY |
USD/TRY | 5 | — |
EURTRY |
EUR/TRY | 5 | — |
USDZAR |
USD/ZAR | 5 | — |
USDMXN |
USD/MXN | 5 | — |
USDCNH |
USD/CNH | 5 | — |
USDINR |
USD/INR | 3 | — |
USDILS |
USD/ILS | 5 | — |
USDTHB |
USD/THB | 3 | — |
USDAED |
USD/AED | 5 | — |
About the data
QUOTC quotes are independently generated OTC prices. They follow the general level of the currency market but are not spot forex prices and do not repeat them tick by tick. Quotes are available 24 hours a day, 7 days a week, including weekends and holidays.
Questions about the integration? Contact us or open a ticket in your dashboard.