Live and webhooks

Darts as it happens, in your own code

Scores for every match in play, a push to your server the moment a result lands, and one feed to keep a copy of everything in step. Checked every five minutes, delivered within the minute after that.

  • Every match in play
  • Four webhook events
  • Signed and retried
In play now
Nothing in play right nowMatches appear here within five minutes of starting.
0 in playGET /v1/live

Three ways to follow it

Use one, or all three together: most apps poll while they show a live screen, and take webhooks for everything else.

Ask for the live board

Every match in play and everything still to come today, with scores and averages. Call it as often as your plan allows while a screen is open.

GET /v1/liveLive and up
Let us tell you

Give us an address and we post to it when a result lands, a fixture is added, a match goes in play or its score moves. Nothing to poll, nothing missed.

POST your-app.com/webhooksLive and up
Keep a copy in step

Everything that changed since your last call: new and corrected matches, players and events. One cursor, so a copy of the archive never drifts.

GET /v1/changes?since=...Live and up

One match, as your app sees it

An example: Littler v Humphries on a Saturday night, and what reaches you through each route.

BoardWebhookFeed
19:02
fixture.addedLittler v Humphries added for 20:00
20:03
match.startedThe match goes in play
20:08
score.changedLittler 2-1 Humphries
20:13
score.changedLittler 3-3 Humphries
20:41
match.finishedLittler 6-3 Humphries, with both averages

The live board and webhooks follow every step. The change feed holds what lasts: the fixture and the result.

How fresh it is

Honest numbers, not "real time".

Live boardEvery 5 min
Webhooks sentWithin 1 min
Results in the archiveMinutes after
Current feed age on the status page

Last night

What went out, Monday 12 October.

Results announced457
Score changes0

Four events, each worth acting on

Pick the ones you want per address. Each arrives as JSON with the ids you need to call for more.

match.finished

A result lands in the archive, with the score, both averages and the match id. Only matches from the last two days, so a backfill of old seasons never fires.

fixture.added

A new match appears on the draw, with its date and time where the organiser has set them.

match.started

A match goes in play. Open your live screen, send your alert.

score.changed

The score moves, checked every five minutes, with the score before it.

{
  "id": "evt_9K2mQ4xTbR7w",
  "type": "match.finished",
  "created": "2026-09-27T20:41:07Z",
  "data": {
    "match_id": "mt_4XQ8RD2P",
    "event": "World Grand Prix",
    "round": "Last 16",
    "winner": {
      "player_id": "pl_VEB8HM", "name": "Luke Littler",
      "score": 6, "average": 104.21
    },
    "loser": {
      "player_id": "pl_K2M4QD", "name": "Luke Humphries",
      "score": 3, "average": 99.87
    }
  }
}
{
  "id": "evt_3Hq8LmV2cP9x",
  "type": "score.changed",
  "created": "2026-09-27T20:13:02Z",
  "data": {
    "fixture_id": "fx_8b1e4c2d90a7f3e1",
    "event": "World Grand Prix",
    "round": "Last 16",
    "players": [
      { "player_id": "pl_VEB8HM", "name": "Luke Littler", "score": 3 },
      { "player_id": "pl_K2M4QD", "name": "Luke Humphries", "score": 3 }
    ],
    "previous_score": "2-1"
  }
}
{
  "id": "evt_7Tn2Wq5KxB4m",
  "type": "match.started",
  "created": "2026-09-27T20:03:11Z",
  "data": {
    "fixture_id": "fx_8b1e4c2d90a7f3e1",
    "event": "World Grand Prix",
    "round": "Last 16",
    "date": "2026-09-27",
    "time": "20:00",
    "players": [
      { "player_id": "pl_VEB8HM", "name": "Luke Littler", "score": 0 },
      { "player_id": "pl_K2M4QD", "name": "Luke Humphries", "score": 0 }
    ]
  }
}
{
  "id": "evt_5Rk9Pz3NvC8q",
  "type": "fixture.added",
  "created": "2026-09-27T19:02:44Z",
  "data": {
    "fixture_id": "fx_8b1e4c2d90a7f3e1",
    "event": "World Grand Prix",
    "round": "Last 16",
    "date": "2026-09-27",
    "time": "20:00",
    "date_confirmed": true,
    "players": [
      { "player_id": "pl_VEB8HM", "name": "Luke Littler" },
      { "player_id": "pl_K2M4QD", "name": "Luke Humphries" }
    ]
  }
}

Signed, so you know it is us

Every post carries a signature made with your secret. Six lines to check it.

$sig = $_SERVER['HTTP_X_DARTSAPI_SIGNATURE'];
parse_str(strtr($sig, ',', '&'), $p);
$body = file_get_contents('php://input');
$want = hash_hmac('sha256',
  $p['t'] . '.' . $body, $secret);
hash_equals($want, $p['v1']);

Retried if you are down

Anything but a 2xx within 10 seconds is tried again, six times over about three hours.

1 min
5 min
15 min
30 min
1 h
1.5 h

After that it shows as failed in your dashboard, one click to send it again.

Every delivery, logged

Your dashboard keeps each attempt for 30 days: what was sent, what your server answered, how long it took.

20:41match.finished200
20:13score.changed200
20:08score.changed500
20:03match.started200
Open your webhooks
Which plans

Live, webhooks and the change feed from $39 a month

All three come with the Live plan, and with Complete and Analyst above it. The Archive plan is for history only and does not include them.

Free key
Live, $39
Archive, $79
Complete, $99
Analyst, $149
See the plans

Stop polling. Start listening.

Add an address in your dashboard and a test delivery goes to it straight away, so you can watch it arrive.

What is in play
GET /v1/live
On the Live plan and up.