JavaScript Examples
These examples work in both browser (native fetch and WebSocket) and Node.js 18+ without any polyfills.
1 — Base Client
// ForexClient.js
export class ForexClient {
static BASE_URL = "https://api.pipdata.net";
constructor(apiKey) {
this.apiKey = apiKey;
}
get #headers() {
return { "X-API-Key": this.apiKey };
}
async #get(path, params = {}) {
const url = new URL(`${ForexClient.BASE_URL}${path}`);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, String(v)));
const resp = await fetch(url, { headers: this.#headers });
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(`${err?.error?.code ?? resp.status}: ${err?.error?.message ?? resp.statusText}`);
}
return resp.json();
}
/** List accessible symbols. */
symbols(params = {}) {
return this.#get("/v1/symbols", params);
}
/** Fetch historical OHLCV bars. */
history(symbol, resolution, countback = 500) {
return this.#get("/v1/history", { symbol, resolution, countback });
}
/** Fetch OHLCV bars between two Unix timestamps. */
historyRange(symbol, resolution, from, to) {
return this.#get("/v1/history", { symbol, resolution, from, to });
}
/** Get available data date ranges. */
coverage(symbol = null) {
return this.#get("/v1/coverage", symbol ? { symbol } : {});
}
/** Check feed health. */
status() {
return this.#get("/v1/status");
}
/** Get API key usage statistics. */
usage(days = 7) {
return this.#get("/v1/usage", { days });
}
}
2 — Fetch Historical Data
import { ForexClient } from "./ForexClient.js";
const client = new ForexClient("fxd_your_key_here");
async function main() {
// Service health check
const st = await client.status();
console.log("Service status:", st.status);
// Fetch last 200 H1 bars for EUR/USD
const data = await client.history("WMMARKETS:ZERO:EURUSD", "60", 200);
if (data.s === "ok") {
// Zip into bar objects
const bars = data.t.map((t, i) => ({
time: new Date(t * 1000).toISOString(),
open: data.o[i],
high: data.h[i],
low: data.l[i],
close: data.c[i],
volume: data.v[i],
}));
const last = bars[bars.length - 1];
console.log(`${data.meta.bar_count} bars received`);
console.log(`Latest bar: ${last.time} close=${last.close.toFixed(5)}`);
}
}
main().catch(console.error);
3 — TradingView Charting Library DataFeed
Drop-in DataFeed implementation for the TradingView Charting Library:
// datafeed.js
const API_KEY = "fxd_your_key_here";
const BASE_URL = "https://api.pipdata.net";
const HEADERS = { "X-API-Key": API_KEY };
const ForexDatafeed = {
// ── onReady ─────────────────────────────────────────────────────────────
onReady(callback) {
fetch(`${BASE_URL}/v1/config`, { headers: HEADERS })
.then(r => r.json())
.then(cfg => setTimeout(() => callback(cfg), 0));
},
// ── searchSymbols ────────────────────────────────────────────────────────
searchSymbols(query, exchange, type, callback) {
fetch(`${BASE_URL}/v1/symbols?search=${encodeURIComponent(query)}&limit=30`, { headers: HEADERS })
.then(r => r.json())
.then(data => callback(
(data.symbols ?? []).map(s => ({
symbol: s.symbol,
full_name: s.symbol,
description: s.description,
exchange: s.exchange,
type: s.type,
}))
));
},
// ── resolveSymbol ────────────────────────────────────────────────────────
resolveSymbol(symbolName, onResolve, onError) {
fetch(`${BASE_URL}/v1/symbols?symbol=${encodeURIComponent(symbolName)}`, { headers: HEADERS })
.then(r => r.json())
.then(s => onResolve(s))
.catch(err => onError(err.message));
},
// ── getBars ──────────────────────────────────────────────────────────────
getBars(symbolInfo, resolution, periodParams, onResult, onError) {
const { from, to, countBack } = periodParams;
const params = new URLSearchParams({ symbol: symbolInfo.name, resolution });
if (countBack) {
params.set("countback", countBack);
if (to) params.set("to", to);
} else {
params.set("from", from);
params.set("to", to);
}
fetch(`${BASE_URL}/v1/history?${params}`, { headers: HEADERS })
.then(r => r.json())
.then(data => {
if (data.s === "no_data") { onResult([], { noData: true }); return; }
if (data.s !== "ok") { onError(data.errmsg || "Unknown error"); return; }
const bars = data.t.map((t, i) => ({
time: t * 1000, // TradingView expects milliseconds
open: data.o[i],
high: data.h[i],
low: data.l[i],
close: data.c[i],
volume: data.v[i],
}));
onResult(bars, { noData: bars.length === 0 });
})
.catch(err => onError(err.message));
},
// ── subscribeBars / unsubscribeBars ──────────────────────────────────────
_ws: null,
_subscribers: {},
subscribeBars(symbolInfo, resolution, onTick, listenerGuid) {
this._subscribers[listenerGuid] = { symbol: symbolInfo.name, onTick };
this._ensureWS();
if (this._ws.readyState === WebSocket.OPEN) {
this._sendSubscribe([symbolInfo.name]);
}
},
unsubscribeBars(listenerGuid) {
delete this._subscribers[listenerGuid];
},
_ensureWS() {
if (this._ws && this._ws.readyState !== WebSocket.CLOSED) return;
this._ws = new WebSocket(`wss://api.pipdata.net/v1/stream?api_key=${API_KEY}`);
this._ws.onopen = () => {
const symbols = [...new Set(Object.values(this._subscribers).map(s => s.symbol))];
if (symbols.length) this._sendSubscribe(symbols);
setInterval(() => {
if (this._ws.readyState === WebSocket.OPEN)
this._ws.send(JSON.stringify({ action: "ping" }));
}, 30_000);
};
this._ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (msg.type !== "bar") return;
for (const sub of Object.values(this._subscribers)) {
if (sub.symbol === msg.symbol) {
sub.onTick({
time: msg.bar.time * 1000,
open: msg.bar.open,
high: msg.bar.high,
low: msg.bar.low,
close: msg.bar.close,
volume: msg.bar.volume,
});
}
}
};
},
_sendSubscribe(symbols) {
this._ws.send(JSON.stringify({ action: "subscribe", symbols, type: "bar" }));
},
// ── getServerTime ────────────────────────────────────────────────────────
// Bar timestamps are broker UTC+3. TradingView must be told the same "now"
// or it treats bars after real-UTC-now as future data and hides them.
getServerTime(callback) {
callback(Math.floor(Date.now() / 1000) + 10800);
},
};
// Initialize TradingView widget
new TradingView.widget({
container_id: "tv_chart_container",
datafeed: ForexDatafeed,
library_path: "/charting_library/",
symbol: "WMMARKETS:ZERO:EURUSD",
interval: "60",
locale: "en",
theme: "dark",
});
4 — Node.js WebSocket
// For Node.js — install: npm install ws
const WebSocket = require("ws");
const API_KEY = "fxd_your_key_here";
const ws = new WebSocket(`wss://api.pipdata.net/v1/stream?api_key=${API_KEY}`);
let pingTimer;
ws.on("open", () => {
console.log("Connected");
ws.send(JSON.stringify({
action: "subscribe",
symbols: ["WMMARKETS:ZERO:EURUSD", "WMMARKETS:ZERO:XAUUSD", "WMMARKETS:ZERO:BTCUSD"],
type: "bar"
}));
pingTimer = setInterval(() => {
if (ws.readyState === WebSocket.OPEN)
ws.send(JSON.stringify({ action: "ping" }));
}, 30_000);
});
ws.on("message", (data) => {
const msg = JSON.parse(data);
if (msg.type === "subscribed") {
console.log(`Subscribed to ${msg.symbols.length} symbols`);
}
if (msg.type === "bar") {
const sym = msg.symbol.split(":")[2];
const time = new Date(msg.bar.time * 1000).toISOString().slice(11, 16);
console.log(`[${time}] ${sym.padEnd(8)} close=${msg.bar.close}`);
}
});
ws.on("close", () => {
clearInterval(pingTimer);
console.log("Disconnected — restarting in 5s");
setTimeout(() => process.exit(1), 5000);
});
ws.on("error", (err) => {
console.error("WebSocket error:", err.message);
});