Football webhooks: stop polling for goals

Most football APIs make you poll. We push match events to your server the second they land. Register an endpoint, handle the retry schedule, and cut hundreds of wasted requests per match.

GOAL API· Engineering

2 min read
Football webhooks: stop polling for goals

Polling burns your quota on nothing

A polling loop asks the same question again and again. Most answers come back identical.

Run one request every 10 seconds across a 2 hour match. You spend 720 requests to catch 3 goals. The other 717 requests told you nothing changed.

Webhooks reverse the direction. You register a URL once. We POST to your server the moment a match event lands.

Events you receive

  • match.started: the match kicked off
  • goal.scored: the score went up, fires once per goal
  • score.changed: the score moved without a goal, because the provider corrected an earlier mistake
  • match.finished: the match ended, including after extra time or penalties
  • match.status_changed: any status transition, such as half time or postponed

Treat goal.scored and score.changed differently. A correction is not a goal. Firing a "GOAL" push notification for a scoring correction annoys your users and teaches them to mute you.

Register an endpoint

curl -X POST https://api.goal-api.com/v1/auth/webhook-endpoints \
  -H "Authorization: Bearer $GOAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/goal-api",
    "events": ["goal.scored", "match.started", "match.finished"]
  }'

The response returns your signing secret one time. Store the secret before you close the connection. Lose the secret and you rotate.

Answer in under 8 seconds

We time out after 8 seconds.

Return 2xx first. Do your work after.

app.post("/webhooks/goal-api", express.raw({ type: "application/json" }), (req, res) => {
  const event = JSON.parse(req.body);

  // Answer immediately, then process off the request path.
  res.sendStatus(200);
  queue.push(event);
});

A slow handler becomes a failed delivery. A failed delivery becomes a retry. Retries multiply your load at the worst possible moment, when goals are arriving.

Retries follow a fixed schedule

Five attempts across roughly half a day: 1 minute, 5 minutes, 25 minutes, 2 hours, 10 hours.

Anything outside 2xx triggers the next attempt.

Make your handler idempotent. A timeout on our side after your database write means you receive the same event twice. Key your processing on the event id and ignore duplicates.

Debug with the delivery log

curl https://api.goal-api.com/v1/auth/webhook-endpoints/ENDPOINT_ID/deliveries \
  -H "Authorization: Bearer $GOAL_API_KEY"

Recent attempts come back with response status codes. Start there when events go missing. Most reports of "webhooks are broken" turn out to be a 500 from the receiving app.

Plan limits

Plan Price Endpoints
Free 0 0
Pro 19 3
Ultra 29 5
Mega 45 10

Each delivery counts as one request against your daily quota. A push we send on your behalf costs the same as a request you send us.

Before you go live

Verify the signature on every delivery. Your endpoint URL is not a secret. Anyone who learns the URL posts fake goals into your app until you check signatures.

Keep reading

Football webhooks: stop polling for goals | GOAL API