ParlayAPI · Docs · Streaming

Webhooks

Register a URL on your server, tell us which events you care about. When those events fire, we POST a signed JSON payload to your URL. No polling, no SSE connection to maintain, just events when they happen.

Available on the Pro tier and above. 5 credits per delivered event.

Supported events

event_typeFires when
arb_flaggedAn arbitrage opportunity exists across two books for any market
ev_alertA bet shows positive expected value vs Pinnacle’s devigged line
line_moveA book’s line moves more than a configurable threshold (default 0.5 points / 5 cents)
live_arbSame as arb_flagged but only during in-play windows

Injury event coming soon. The underlying injury data feed is being wired up; until that ships, injury as an event_type returns 400. Subscribe to one of the four above in the meantime.

Register a webhook

POST /v1/webhooks
{
    "url": "https://yourapp.com/parlay-webhook",
    "events": ["arb_flagged", "ev_alert"],
    "sport_filter": ["basketball_nba", "americanfootball_nfl"]
}

Body fields:

fieldtypedescription
urlstring (https URL)Your endpoint that will receive the signed POST.
eventsarray of stringsSubscribe to one or more of the supported event_types above.
sport_filterarray of sport_keys (optional)If set, only events for these sports fire. Omit for all sports.

Response includes a secret. Save it. You’ll need it to verify signatures. We never show it again.

{
    "id": 13,
    "url": "https://yourapp.com/parlay-webhook",
    "events": ["arb_flagged", "ev_alert"],
    "sport_filter": ["basketball_nba", "americanfootball_nfl"],
    "secret": "whsec_8f2a1b9c4d6e0f7a3b5c8d1e",
    "is_active": true,
    "created_at": "2026-05-09T05:00:00Z"
}

Payload shape

{
    "id": "evt_9c3d8a1f",
    "event_type": "arb_flagged",
    "timestamp_ms": 1778214028111,
    "data": {
        "sport_key": "basketball_nba",
        "match_id": "0042500222",
        "home_team": "Lakers",
        "away_team": "Thunder",
        "market": "h2h",
        "edge_pct": 0.014,
        "legs": [
            {"book": "draftkings", "side": "Lakers", "price": +145},
            {"book": "fanduel", "side": "Thunder", "price": -130}
        ]
    }
}

Signature verification

Every webhook request includes a header:

X-Parlay-Signature: t=1778214028,v1=8a3f2d1c4b6e9f0a8d2c5b3f1e9c7d6b4a8f0e2c5b3d8a1f9e7c4b6d2a8f3e1c

To verify (Python):

import hmac, hashlib, time

def verify_webhook(secret: str, raw_body: bytes, header: str, max_age=300) -> bool:
    parts = dict(p.split('=', 1) for p in header.split(','))
    t = int(parts['t'])
    if abs(time.time() - t) > max_age:
        return False
    expected = hmac.new(
        secret.encode(),
        f'{t}.'.encode() + raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, parts['v1'])

JavaScript:

import crypto from 'crypto';

function verifyWebhook(secret, rawBody, header, maxAge = 300) {
    const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
    const t = parseInt(parts.t, 10);
    if (Math.abs(Date.now() / 1000 - t) > maxAge) return false;
    const expected = crypto.createHmac('sha256', secret)
        .update(`${t}.${rawBody}`)
        .digest('hex');
    return crypto.timingSafeEqual(
        Buffer.from(expected),
        Buffer.from(parts.v1)
    );
}

Retries and disabling

Failed delivery (any non-2xx response) is retried 3 times: immediate, +30s, +5min.

5 consecutive total failures auto-disables the webhook and emails the owner. You can re-enable from the dashboard once you’ve fixed the receiving end.

Test a webhook

POST /v1/webhooks/{webhook_id}/test

Sends a synthetic payload with "event_type": "test" so you can verify your endpoint and signature logic without waiting for real events.

List, update, delete

GET /v1/webhooks                    # list yours
GET /v1/webhooks/{id}               # one
PATCH /v1/webhooks/{id}             # update url, events, sport_filter, active
DELETE /v1/webhooks/{id}            # remove
POST   /v1/webhooks/{id}/test       # send a real test payload to the URL; returns delivered + upstream status

Tier limits

TierMax webhooksMax events/min
Pro560
Business25300
Enterprisecontactcontact