ピナクルオッズWebSocketのPython例
回答: websocketsをインストールし、PINNFEED_KEYを設定して、x-api-keyヘッダー付きでwss://pinnfeed.io/v1/streamに接続します。subscribeメッセージを送り、snapshotとdeltaを順に反映し、pingには{"type":"pong"}で応答し、切断時は指数バックオフで再接続します。Builderプラン(月額99ユーロ)が必要で、FreeとHobbyのキーは403 plan_lacks_wsになります。
最小構成のクライアント#
pip install websockets
export PINNFEED_KEY="ps_live_..."
import asyncio, json, os, random
import websockets
WS = "wss://pinnfeed.io/v1/stream"
KEY = os.environ["PINNFEED_KEY"]
async def main():
delay = 1
while True:
try:
async with websockets.connect(WS, additional_headers={"x-api-key": KEY}, max_size=None) as ws:
await ws.send(json.dumps({
"type": "subscribe",
"sports": ["soccer", "tennis"],
"markets": ["moneyline"],
"main": True,
"specials": False,
}))
async for raw in ws:
msg = json.loads(raw)
if msg["type"] == "ping":
await ws.send('{"type":"pong"}')
elif msg["type"] == "snapshot" and msg.get("final"):
print("snapshot ready")
delay = 1
elif msg["type"] == "delta":
for c in msg["changes"]:
if c["kind"] == "market" and c["op"] == "upsert":
m = c["market"]
if (m["maxStake"] or 0) >= 100:
print(m["id"], [(o["name"], o["decimal"], o["fair"]) for o in m["outcomes"]])
except (OSError, websockets.ConnectionClosed) as e:
print("disconnected", e, "retry in", delay, "s")
await asyncio.sleep(delay + random.random())
delay = min(delay * 2, 30)
asyncio.run(main())
各アウトカムのフィールド:decimal、american、fair(マージンなし)、fairProb。マーケットIDはeventId:keyです。maxStake >= 100の条件で流動性の低いマーケットを除外しています。フレームの一覧:WebSocketドキュメント。
クローズコード#
| コード | 意味 |
|---|---|
4002 no_subscription | 30秒以内にsubscribeがない |
4008 slow_consumer | クライアントの処理が遅い(サーバー側バッファが16MB超) |
4001 key_revoked | キーが失効:再接続を繰り返さない |
Builderは料金ページから3日間の無料トライアルを開始できます。カード登録が必要で、解約しない場合は3日目の後に課金されます。まずは無料のRESTキーからでも構いません。関連:プッシュとポーリング。
18歳以上の方を対象とした教育目的のコンテンツです。技術ドキュメントであり、予想や利益の約束ではありません。
よくある質問#
FreeキーでWebSocketが403になるのはなぜですか?#
FreeとHobbyはRESTのみです。プッシュ配信にはBuilderが必要です。Free(30秒遅延)やHobby(リアルタイム)では/v1/oddsをポーリングしてください。
pingに応答する必要はありますか?#
はい。サーバーは20秒ごとにpingを送り、60秒間pongがないと接続を切断します。
同時接続数は?#
Builderは3本、Proは10本です。超えると429 connection_limitになります。