Webhooks
Be called when a followed channel, show or event changes or starts.
On the paid plans, register an https address and we call it when something you follow happens, instead of you asking every few minutes. An account has up to 10 webhooks; make them with Create a webhook.
The events
| Event | Sent when | Follows |
|---|---|---|
schedule.changed | A slot on a followed channel was added, removed, moved or retitled, within about half an hour; up to 500 changes a call, the same rows as the change feed | channels or countries |
show.starting | A followed show starts within 15 minutes on a channel | shows, and countries to narrow it |
event.starting | A followed team's or league's event starts within 15 minutes, with every channel showing it | teams or leagues, and countries to narrow it |
curl -X POST https://api.tvguideapi.com/v1/webhooks -H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/tv", "events": ["event.starting"], "filter": {"teams": ["arsenal"], "countries": ["GB"]}}'The answer carries the webhook's secret in full, once. Keep it: it signs every call we make to you.
What we send
A POST with a JSON body, the same for every event:
{
"id": "evt_3f9c2a71b0d4e815",
"type": "show.starting",
"created": "2026-10-18T18:45:02Z",
"data": {
"show_id": "sh_7qk3m2xa",
"listing": "ls_k2m9q4ta",
"channel": "ch_9d4kq2ma",
"channel_name": "BBC One",
"country": "GB",
"start": "2026-10-18T19:00:00Z",
"title": "EastEnders"
}
}With three headers: X-TVG-Event (the event), X-TVG-Delivery (one id per delivery, the same on every try of it) and X-TVG-Signature.
Checking the signature
X-TVG-Signature is t=<unix time>,v1=<hex>, where v1 is an HMAC-SHA256 of the time, a full stop and the raw body, keyed with your secret. Compare it, and refuse a time more than five minutes old:
<?php
$body = file_get_contents('php://input');
preg_match('/t=(\d+),v1=([0-9a-f]+)/', $_SERVER['HTTP_X_TVG_SIGNATURE'] ?? '', $m);
$ok = $m && abs(time() - (int) $m[1]) < 300
&& hash_equals(hash_hmac('sha256', $m[1] . '.' . $body, 'whsec_YOUR_SECRET'), $m[2]);
http_response_code($ok ? 200 : 400);Answering, and retries
Answer with any 2xx within 10 seconds; do the work after answering. Anything else is tried again, 8 times over about 3 days (after 1 minute, 5 minutes, 30 minutes, 2, 6, 12, 24 and 24 hours). A 4xx other than 408 or 429 means the call will never be accepted, so that delivery stops at once. After 20 failures in a row the webhook is turned off and we email you. Deliveries can arrive more than once and out of order: use X-TVG-Delivery or the event's id to keep one copy.
Testing and reading back
Test a webhook sends the newest real event of that webhook now, marked "test": true, and shows what your server answered. Get a webhook lists its last 50 calls with each answer.
Addresses we call
https only, on the public internet: an address that resolves to a private, loopback or link-local network is refused, when it is made and on every call.