On the wire · 05:11:21 UTC Live 585 · Prematch 14,343 · Markets 57,081 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Tutorial · Deno · WebSocket

Pinnacle odds WebSocket client in Deno

Updated · PinnFeed · 2 min read

Answer: Deno ships a standard WebSocket, so no package is needed. The standard constructor has no headers option, so on the server pass the key as ?key= read from an environment variable, subscribe in onopen, reply {"type":"pong"} to each ping (every 20 s), and keep a Map of markets updated from snapshot and delta frames. Run with deno run --allow-net --allow-env stream.ts. Builder or Pro key required.

stream.ts#

const KEY = Deno.env.get('PINNFEED_KEY');   // ps_live_...
if (!KEY) throw new Error('set PINNFEED_KEY');
const markets = new Map<string, any>();
let backoff = 1000;

function connect() {
  const ws = new WebSocket(`wss://pinnfeed.io/v1/stream?key=${encodeURIComponent(KEY!)}`);
  ws.onopen = () => {
    backoff = 1000;
    ws.send(JSON.stringify({ type: 'subscribe', sports: ['basketball'], markets: ['spread', 'total'], main: true }));
  };
  ws.onmessage = (ev) => {
    const msg = JSON.parse(ev.data);
    if (msg.type === 'ping') return ws.send('{"type":"pong"}');
    if (msg.type === 'snapshot') {
      if (msg.part === 1) markets.clear();
      for (const m of msg.markets) markets.set(m.id, m);
      if (msg.final) console.log('snapshot ready:', markets.size, 'markets');
    } else if (msg.type === 'delta') {
      for (const ch of msg.changes) {
        if (ch.kind !== 'market') continue;
        if (ch.op === 'remove') markets.delete(ch.id);
        else markets.set(ch.market.id, ch.market);
      }
    } else if (msg.type === 'error') console.error(msg.code, msg.message);
  };
  ws.onclose = (ev) => {
    console.log('closed', ev.code, ev.reason);
    if (ev.code === 4001) return;                 // key revoked: stop
    setTimeout(connect, backoff);
    backoff = Math.min(backoff * 2, 30000);
  };
}
connect();

The key in the URL never leaves your server here, but keep it out of logs. Close codes: 4002 no subscribe in 30 s, 4008 slow consumer (buffer over 16 MB), 4001 key revoked.

Plans#

WebSocket on Builder (€99/mo, 3 connections) and Pro. 3-day trial; a card is required and it is charged after day 3 unless you cancel. Same pattern in TypeScript/Node and Bun.

Educational content for adults (18+).

Questions people ask#

Can I use Deno Deploy?#

Deploy is request-driven; for a 24/7 stream, run the script on a VM or container. For scheduled REST, see Supabase Edge Functions.

Why not set the x-api-key header?#

The web-standard constructor has no headers argument. The URL key works the same on the server.

Does Deno support compression?#

PinnFeed only compresses when the client negotiates permessage-deflate; see compression.

The WebSocket plan: Builder, €99/mo.

Real-time Pinnacle odds pushed over one WebSocket, all 13 sports, every market, no-vig fair odds and closing lines. 3 days free, cancel in one click.

Form K-1 · Free API key

Get your free API key.

One field. No password, no card: enter your email, click the link we send, and your key is on screen.

By continuing you agree to the Terms and Privacy Policy.

One free key per email. Link valid for 20 minutes.