On the wire · 05:11:55 UTC Live 587 · Prematch 14,341 · Markets 57,329 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Tutorial · Data · DuckDB

Odds de fecho da Pinnacle em DuckDB e Parquet

Atualizado em · PinnFeed · 1 min de leitura

Resposta: no Builder ou Pro, rode um job diário pequeno que pagina GET /v1/closing?date=YYYY-MM-DD (até 2.000 linhas por chamada), achata market e outcomes, e escreve um Parquet por dia. O DuckDB consulta a pasta com SQL, sem servidor de base de dados. Os registos existem desde que o PinnFeed guarda fechos.

1. Daily export (Python)#

import os, sys, requests, pandas as pd
H = {"x-api-key": os.environ["PINNFEED_KEY"]}  # ps_live_...

def closing_day(day, sport):
    rows, offset = [], 0
    while True:
        r = requests.get("https://pinnfeed.io/v1/closing", headers=H, timeout=30,
                         params={"date": day, "sport": sport, "limit": 2000, "offset": offset})
        r.raise_for_status()                     # 403 plan_lacks_history on Free/Hobby
        batch = r.json()["closing"]
        rows += batch
        if len(batch) < 2000:
            return rows
        offset += 2000

day = sys.argv[1]                                # e.g. 2026-11-08
out = []
for row in closing_day(day, "soccer"):
    m = row["market"]
    for o in m["outcomes"] or []:
        out.append({
            "event_id": row["eventId"], "league": row["league"]["name"], "home": row["home"], "away": row["away"],
            "start_time": row["startTime"], "captured_at": row["capturedAt"],
            "market": m["type"], "period": m["period"], "line": m["line"], "main": m["main"],
            "margin": m["margin"], "max_stake": m["maxStake"],
            "outcome": o.get("name"), "decimal": o.get("decimal"), "fair": o.get("fair"),
        })
os.makedirs("closing", exist_ok=True)
pd.DataFrame(out).to_parquet(f"closing/soccer-{day}.parquet", index=False)   # needs pyarrow
print(len(out), "outcome rows")

2. Query with DuckDB#

-- duckdb shell, from the folder that contains closing/
SELECT league, avg(margin) AS avg_margin
FROM read_parquet('closing/*.parquet')
GROUP BY 1
ORDER BY 2 DESC
LIMIT 20;

CTA#

Builder (€99/mês). Teste de 3 dias; cartão requerida. REST gratis: signup. Related: closing/CLV, backtesting.

Perguntas frequentes#

Free/Hobby chegam?#

Não — /v1/closing devolve 403 plan_lacks_history fora de Builder/Pro.

Conteúdo educativo para adultos?#

Sí (18+).

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

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.