On the wire · 04:58:07 UTC Live 568 · Prematch 14,378 · Markets 56,837 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Tutorial · Cloudflare · REST

Odds da Pinnacle num Cloudflare Worker

Atualizado em · PinnFeed · 2 min de leitura

Resposta: guarde a chave com wrangler secret put PINNFEED_KEY. O handler fetch do Worker faz proxy de /v1/odds para o seu front end com um cache curto no edge, então a chave nunca chega ao navegador; o handler scheduled roda num cron trigger, chama /v1/drops e publica as quedas novas num webhook do Discord. É tudo REST, então funciona a partir do Free (30 s de atraso).

wrangler.toml#

name = "pinnfeed-worker"
main = "src/index.js"
compatibility_date = "2026-11-01"

[triggers]
crons = ["*/2 * * * *"]
npx wrangler secret put PINNFEED_KEY      # ps_live_...
npx wrangler secret put DISCORD_WEBHOOK   # https://discord.com/api/webhooks/...
npx wrangler deploy

src/index.js#

const API = 'https://pinnfeed.io/v1';

export default {
  async fetch(req, env, ctx) {
    const url = new URL(req.url);
    if (url.pathname !== '/odds') return new Response('not found', { status: 404 });
    const sport = url.searchParams.get('sport') || 'soccer';
    const cache = caches.default;
    const cacheKey = new Request(`${url.origin}/odds?sport=${sport}`);
    let res = await cache.match(cacheKey);
    if (!res) {
      const r = await fetch(`${API}/odds?sport=${sport}&market=moneyline&main=true&limit=50`, {
        headers: { 'x-api-key': env.PINNFEED_KEY },
      });
      res = new Response(r.body, { status: r.status, headers: { 'content-type': 'application/json', 'cache-control': 'max-age=5' } });
      if (r.ok) ctx.waitUntil(cache.put(cacheKey, res.clone()));
    }
    return res;
  },

  async scheduled(_event, env) {
    const r = await fetch(`${API}/drops?sport=soccer&minutes=2&min_pct=5&limit=10`, {
      headers: { 'x-api-key': env.PINNFEED_KEY },
    });
    if (!r.ok) return;
    const { drops } = await r.json();
    if (!drops.length) return;
    const lines = drops.map((d) => `${d.home} v ${d.away}: ${d.market} ${d.outcome} ${d.from} → ${d.to} (${d.dropPct}%)`);
    await fetch(env.DISCORD_WEBHOOK, {
      method: 'POST', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ content: lines.join('\n').slice(0, 1900) }),
    });
  },
};

Como a janela do cron e a janela minutes são ambas de 2, cada queda é publicada mais ou menos uma vez; guarde os ids no KV se precisar de deduplicação estrita.

Limites a respeitar#

O Free permite 1 requisição/s e 1.000 por dia, o Hobby 2/s e o Builder 10/s. O cache do edge evita que picos de tráfego virem chamadas à API. Para push de verdade, mantenha o WebSocket num servidor sempre ligado com o Builder (€99/mês). Teste de 3 dias; cartão obrigatório, cobrado após o dia 3 a menos que você cancele.

Relacionado: apps de navegador e CORS, bot de Discord, API de odds em queda.

Conteúdo educativo para maiores de 18 anos. Sem palpites nem lucro garantido. No Brasil, aposte apenas em operadores autorizados pela SPA/MF.

Perguntas frequentes#

Um Worker consegue manter o WebSocket do PinnFeed aberto?#

Um Worker comum dura o tempo da requisição. Um Durable Object pode manter sockets, mas para o stream um pequeno servidor sempre ligado é mais simples.

O cache do edge entrega odds velhas?#

No máximo pelo max-age que você definir (5 s aqui). Diminua, ou não faça cache nas visões ao vivo.

O cron conta na minha cota diária?#

Sim, cada execução é uma requisição: a cada 2 minutos são 720 por dia, dentro das 1.000 do Free.

O plano WebSocket: Builder, €99/mês.

Odds da Pinnacle em tempo real por um único WebSocket: os 13 esportes, todos os mercados, odds justas sem margem e odds de fechamento. 3 dias grátis, cancele com um clique.

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.