On the wire · 05:09:02 UTC Live 584 · Prematch 14,344 · Markets 57,011 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Tutorial · Data · DuckDB

Cuotas de cierre de Pinnacle en DuckDB y Parquet

Actualizado el · PinnFeed · 1 min de lectura

Respuesta: en Builder o Pro, ejecuta un job diario pequeño que pagina GET /v1/closing?date=YYYY-MM-DD (hasta 2.000 filas por llamada), aplana market y outcomes, y escribe un Parquet por día. DuckDB consulta luego la carpeta con SQL, sin servidor de base de datos. Los registros existen desde que PinnFeed guarda cierres.

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/mes). Prueba de 3 días; tarjeta requerida. REST gratis: signup. Related: closing/CLV, backtesting.

Preguntas frecuentes#

¿Bastan Free/Hobby?#

No — /v1/closing devuelve 403 plan_lacks_history fuera de Builder/Pro.

¿Contenido educativo para adultos?#

Sí (18+).

Contenido educativo para mayores de 18 años. Sin pronósticos ni ganancias garantizadas. En España, apuesta solo con operadores con licencia de la DGOJ.

El plan WebSocket: Builder, 99 €/mes.

Cuotas de Pinnacle en tiempo real por un único WebSocket: los 13 deportes, todos los mercados, cuotas justas sin margen y cuotas de cierre. 3 días gratis y cancelas con un clic.

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.