Registers an HTTPS endpoint for signed event notifications: Pinnacle line moves, positive EV prices, game start and final, and graded player props. Returns the signing secret once.
Registers an HTTPS endpoint for signed event notifications: Pinnacle line moves, positive EV prices, game start and final, and graded player props. Returns the signing secret once.
Coverage by event type (a sport listed in filters.sports that one of your event types does not cover is a 400): line.moved: mlb, nba, ncaab, ncaaf, nfl, nhl, soccer, tennis, wnba; ev.found: cs2, dota2, lol, mlb, mma, nba, ncaab, ncaaf, nfl, nhl, soccer, tennis, valorant, wnba; game.started: mlb, nba, ncaab, nhl, soccer, tennis, nfl, ncaaf; game.final: mlb, nba, ncaab, nhl, soccer, tennis, nfl, ncaaf; props.graded: mlb, nfl.
line.moved: Pinnacle's main moneyline, spread or total for a game that has not started moved by at least your step since the last line.moved you were sent for that market, so small moves add up. price_step_pp is the change in implied probability in percentage points (0.5, 1, 2, 3, 5), point_step the change in the spread or total line (0.5, 1, 1.5, 2, 3). A line change smaller than point_step restarts the price comparison at the new line. After a line.moved the market is quiet for 60 seconds, and a move inside that window is sent once at its end if it still clears your step. data: { sport, league, event_id, source_event_id, home_team, away_team, commence_time, book, market, reason, previous, current, max_probability_change_pp, point_change, thresholds }, where previous and current map each side (home, away, draw, over, under) to { price, point }.
ev.found: a price on the EV board became tradeable at or above your min_ev, in percent (1, 2, 3, 5, 8), by the same fair-value math as the EV endpoint. Tradeable means it clears the trade bar: a sportsbook price needs an EV of at least 2%; an exchange or prediction-market price needs at least 4% on its cost, a bid, a spread of 4 cents or less and $50 at the ask, so a min_ev below those floors adds nothing. It fires when the price crosses min_ev, again only after its EV has fallen more than 1 point below min_ev, or the price has been gone or stopped clearing the trade bar for two consecutive reads (about 40 seconds), and at most once per 15 minutes for one event, venue and side. After an alert, the same price for that event, venue and side is not sent again for 2 hours unless its EV has risen by at least 1 point. Nothing fires while the odds behind the board are out of date. kinds narrows to book (sportsbook) or venue (exchange and prediction market) prices; venues narrows to the listed keys, which must be among fanduel, draftkings, betmgm, bet365, caesars, kalshi, novig, 1xbet, betonline, betr, bovada (other keys can never produce an event, so they are refused). data: { sport, league, event_id, odds_event_id, home_team, away_team, commence_time, min_ev, signal: { kind, venue, side, team, price, implied_probability, fair_probability, fair_low, ev_percent, ev, fee, route, size, link, quote_age_ms }, board_age_ms }.
game.started: sent once per game when the live scores show it in play on two reads at least 15 seconds apart. game.final: sent once per game when they show it final on two reads at least 60 seconds apart and the final is believable: in tennis a player has won 2 sets, so a match that ends by retirement or walkover before that is not announced; in the other sports the game has run at least 80 minutes (soccer), 90 minutes (nba), 80 minutes (ncaab), 90 minutes (nhl), 90 minutes (mlb), 120 minutes (nfl), 150 minutes (ncaaf) from its start. A game reported final earlier than that, as an interrupted one can be, is not announced then; if the live scores show it in play again, its final is sent when it ends. A postponed or cancelled game never sends it. Status changes can trail the real moment by a few minutes. data: { sport, event_id, league, home_team, away_team, start_time, status: { state, detail }, score: { home, away } }; for tennis the score is sets won.
props.graded: sent when the first player prop results of a game are confirmed (complete: false) and again when every result of the game is confirmed (complete: true); a game confirmed in one pass sends a single event with complete: true. Games graded before you subscribed are not replayed, except that a game graded in the 30 minutes before can be, and a game graded during an interruption of the service longer than 30 minutes may not be announced. data: { sport, event_id, game_date, complete, confirmed_count, pending_count, results_path }; read the results from results_path.
Every delivery is a POST of the JSON { id, type, api_version, created, occurred_at, expires_at, endpoint_id, data } (api_version 2026-10-01) with the headers Owls-Webhook-Id (the event id), Owls-Event-Type, Owls-Delivery-Attempt and Owls-Signature.
Owls-Signature is t=<unix seconds>,v1=<hex>. Compute HMAC-SHA256 with your signing secret (the whole string, whsec_ included, as UTF-8) over t, a period, and the raw request body, then compare it with v1 in constant time, and reject a t more than 5 minutes from your clock. The SDKs ship verifyWebhookSignature (TypeScript) and verify_webhook_signature (Python).
Delivery is at least once and unordered: dedupe on id, which is identical on every retry, and do not rely on one event arriving before another.
Answer with any 2xx within 10 seconds (3 to connect). Anything else is a failure, a redirect included: redirects are never followed. line.moved and ev.found retry after about 5 s, 20 s, 1 m and then expire; the other types retry after about 10 s, 1 m, 5 m, 15 m, 1 h, 3 h, 6 h, 12 h. Retry-After is honoured. Nothing is sent after expires_at: line.moved 5 minutes, ev.found 2 minutes, game.started 2 hours, game.final 24 hours, props.graded 24 hours after the event.
410 Gone disables the endpoint. After 5 failures in a row its deliveries pause briefly (30 seconds, doubling up to 30 minutes). After 50 failures in a row and 72 hours without a success it is disabled (status auto_disabled); PATCH { "enabled": true } resumes it.
The url must be https on the default port with a public hostname. IP addresses, private or reserved addresses and internal hostnames are refused, at registration and on every delivery.
Endpoints per plan: MVP 3, Hall of Fame 10. Events per account: MVP 120 a minute and 20,000 a day, Hall of Fame 600 a minute and 100,000 a day; events over the cap are dropped, and so are new line.moved and ev.found events for an endpoint with more than 1,000 undelivered. At most 30 endpoint changes per minute per account.
Request
- Base URL: https://api.owlsinsight.com
- URL: https://api.owlsinsight.com/api/v1/webhooks
- Auth: HTTP bearer
Request body
Response
Created
Changes
No recorded changes to this operation across all 1 revision of this API.