DEV Community

orbistats
orbistats

Posted on

Building a Head-to-Head Match History Lookup in PHP

Why you'd need this

A lot of PHP work in this space isn't live data at all, it's the boring but genuinely useful stuff: a head-to-head page showing how two teams have done against each other historically, a stats widget on a match preview page, a backtest script checking how a pricing model would have performed last season. None of that needs a WebSocket. It needs a clean historical data call and something sensible done with the response.

This is a short walkthrough of pulling historical match data in PHP using cURL, no framework required.

Getting a key

You'll need an API key first. There's a free tier that covers testing this without a sales conversation: https://orbistats.com/signup.html

Worth checking the sandbox first if you want to see the actual response shape before writing a parser around it, no account needed for that part: https://orbistats.com/developers/sandbox.html

The request

<?php

$apiKey = getenv('ORBISTATS_API_KEY');
$baseUrl = 'https://api.orbistats.com/v1';

function fetchHeadToHead(string $teamA, string $teamB, string $apiKey, string $baseUrl): array
{
    $url = $baseUrl . '/football/head-to-head?team_a=' . urlencode($teamA) . '&team_b=' . urlencode($teamB);

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $apiKey,
    ]);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);

    $response = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($statusCode !== 200) {
        throw new RuntimeException("Request failed with status {$statusCode}");
    }

    return json_decode($response, true) ?? [];
}

$history = fetchHeadToHead('Manchester City', 'Arsenal', $apiKey, $baseUrl);

foreach ($history['data'] ?? [] as $match) {
    echo $match['date'] . ': ' . $match['home_team'] . ' vs ' . $match['away_team']
        . ' (' . $match['home_score'] . '-' . $match['away_score'] . ')' . PHP_EOL;
}
Enter fullscreen mode Exit fullscreen mode

A note before you copy this: the endpoint path football/head-to-head and the response field names (data, date, home_team, home_score, and so on) are written based on the documented pattern for historical lookups, not a response I've inspected directly. Verify the real endpoint path and field names against the live API reference before shipping this, since exact naming can differ from what's assumed here: https://orbistats.com/developers/api-reference.html

Caching, because you almost certainly should

Historical results for a past match don't change. There's no reason to hit the API for the same head-to-head lookup on every page load. A simple file or Redis cache keyed on the two team names, with a long TTL, cuts your request volume significantly for anything showing this on a public-facing page:

$cacheKey = 'h2h_' . md5($teamA . $teamB);
$cached = apcu_fetch($cacheKey);

if ($cached === false) {
    $cached = fetchHeadToHead($teamA, $teamB, $apiKey, $baseUrl);
    apcu_store($cacheKey, $cached, 86400); // 24 hours
}
Enter fullscreen mode Exit fullscreen mode

This matters more than it sounds like it should. Historical data endpoints are the easiest ones to accidentally hammer because the temptation is to just call them inline on every request, and they're also the ones where caching costs you nothing in staleness since the underlying data isn't changing.

When historical data isn't enough

If the page you're building also needs to show what's happening in a match right now, not just what happened in past meetings, that's a different endpoint and a different update pattern entirely, live rather than static. Worth reading the general documentation on the difference before assuming one API call covers both: https://orbistats.com/developers/documentation.html

Where to go from here

This covers one endpoint out of a broader historical dataset, standings history, player statistics over time, season-by-season breakdowns. The full reference is worth a look once this basic version is working: https://orbistats.com/developers/api-reference.html

Curious if anyone else here is still doing this kind of thing in plain PHP with cURL, or if you've moved everything over to Guzzle by default at this point and it's not worth the discussion I'm implying it is.

Top comments (0)