Pinnacle odds WebSocket client in Bun
Answer: Bun's built-in WebSocket accepts a non-standard headers option, so you can send x-api-key instead of putting the key in the URL. Subscribe on open, answer each ping with {"type":"pong"}, and compare each market upsert with the previous price to log moves. Run with bun run stream.ts. Builder or Pro key required.
stream.ts#
const last = new Map<string, number>(); // market id + outcome -> decimal
const ws = new WebSocket('wss://pinnfeed.io/v1/stream', {
headers: { 'x-api-key': process.env.PINNFEED_KEY! }, // ps_live_...
} as any);
ws.addEventListener('open', () => {
ws.send(JSON.stringify({ type: 'subscribe', sports: ['tennis'], markets: ['moneyline'], main: true }));
});
ws.addEventListener('message', (ev) => {
const msg = JSON.parse(String(ev.data));
if (msg.type === 'ping') return ws.send('{"type":"pong"}');
const list = msg.type === 'snapshot' ? msg.markets
: msg.type === 'delta' ? msg.changes.filter((c: any) => c.kind === 'market' && c.op === 'upsert').map((c: any) => c.market)
: [];
for (const m of list) {
for (const o of m.outcomes) {
const k = `${m.id}|${o.name}`;
const prev = last.get(k);
if (prev && o.decimal && Math.abs(o.decimal / prev - 1) >= 0.05) {
console.log(new Date().toISOString(), m.eventId, o.name, prev, '→', o.decimal, 'maxStake', m.maxStake);
}
last.set(k, o.decimal);
}
}
});
ws.addEventListener('close', (ev) => console.log('closed', ev.code, ev.reason));
A threshold check like m.maxStake >= 100 helps ignore thin markets. Add a reconnect with backoff for long runs: reconnect pattern.
Plans#
Builder (€99/mo) for WebSocket push, all 13 sports, 3 connections. 3-day trial; a card is required and it is charged after day 3 unless you cancel. Server-side drop alerts without a socket: /v1/drops.
Educational content for adults (18+). No tips, no guaranteed profit.
Questions people ask#
Will this code run in Node?#
Node's global WebSocket has no headers option; use the ws package there: Node example.
Why as any?#
The headers option is Bun-specific, so the DOM type definitions do not include it.
Is 5% a good alert threshold?#
It is only an example; tune it to your markets.