Skip to content
Integration

Adding football data to a WordPress site

How to show fixtures and tables on WordPress safely, using server-side requests, transients and scheduled refreshes.

FootballAPI.net editorial team

Written and reviewed by the organisation

Published 30 Oct 2026Updated 1 Nov 20265 min read

FootballAPI.net — Adding football data to a WordPress site, illustrated by a floodlit stadium pitch

WordPress powers a huge number of club, fan and local news sites. Adding a fixture list or league table to those sites is a common request, and it is easy to do badly: a plugin that calls the API on every page view, or a script that puts the key in page source.

This guide outlines a safe approach using standard WordPress features. It assumes a small custom plugin, which is easier to maintain than editing a theme and survives theme changes.

Store the key safely

Put the key in your site configuration as a constant rather than in the database or the theme. Configuration files are outside the web root on many hosts and are not exported with content. Read the constant in your plugin and refuse to run if it is missing.

Never output the key in HTML, JavaScript or REST responses. Administrators should be able to see that a key is configured, not its value.

If several people manage the site, document where the key lives and how to rotate it from the FootballAPI.net dashboard.

  • Key in configuration, not the database
  • Never echoed into pages
  • Rotation documented for the team

Fetch server-side and cache

Use the WordPress HTTP API to make requests from PHP, with the X-API-Key header and a short timeout. Check the response code before decoding JSON. On success, store the decoded data in a transient or an option with the time it was fetched.

Pages should read the cached copy only. If you fetch during page rendering, every visitor potentially triggers a request, which is slow and wasteful.

function refresh_football_standings($league_id) {
    if (!defined('FOOTBALLAPI_BASE_URL') || !defined('FOOTBALLAPI_KEY')) {
        return new WP_Error('football_config', 'Missing server-side API configuration');
    }
    $url = add_query_arg('leagueId', $league_id,
        rtrim(FOOTBALLAPI_BASE_URL, '/') . '/api/v1/football/standings');
    $response = wp_remote_get($url, array(
        'timeout' => 15,
        'headers' => array('X-API-Key' => FOOTBALLAPI_KEY, 'Accept' => 'application/json'),
    ));
    if (is_wp_error($response)) return $response;
    if (wp_remote_retrieve_response_code($response) !== 200) {
        return new WP_Error('football_http', 'Refresh failed; retain the previous snapshot');
    }
    $body = json_decode(wp_remote_retrieve_body($response), true);
    if (!is_array($body) || !array_key_exists('data', $body) || !array_key_exists('meta', $body)) {
        return new WP_Error('football_shape', 'Unexpected response envelope');
    }
    // Add feed-specific validation before saving records used by your templates.
    update_option('football_standings_' . sanitize_key($league_id),
        array('result' => $body, 'fetched_at' => time()), false);
    return true;
}

Scheduled refreshes

WordPress cron runs when pages are visited, which is unreliable on quiet sites and bursty on busy ones. Where your host allows, trigger WordPress cron from a real system scheduler, and schedule your refresh events at sensible intervals per feed: fixtures a few times a day, standings after matches, squads weekly.

Keep the previous cached copy if a refresh fails. Store it as an option, not only a transient, so it persists even if transients are flushed by a caching plugin.

Rendering with shortcodes or blocks

Provide a shortcode or a block that renders the fixtures or table from the cache. Let editors choose the competition from a dropdown populated by your stored competition directory, rather than typing identifiers. Output semantic HTML tables for standings and lists for fixtures.

Include a small last-updated line in the output. Escape all output with the WordPress escaping functions, even though the data comes from a trusted source; defence in depth costs nothing.

  • Editors choose competitions from a list
  • Semantic tables and lists
  • Escape everything on output

Images

Crests and player photos can be referenced through FootballAPI.net's media route. Add width, height and lazy loading attributes. Do not import them into the media library, which copies them into your uploads folder and may breach display terms.

Provide text fallbacks so a failed image does not leave an empty gap in a table.

Page caching plugins

Full-page caching plugins work well with this pattern, since your pages are generated from stored data. Make sure the page cache expires at least as often as your data refreshes, or purge relevant pages after a refresh, so readers do not see an old table for hours after a match.

Security and updates

Keep the plugin small and readable. Validate every setting on save, restrict settings pages to administrators and use nonces on forms. If you distribute the plugin to other sites, never ship a key with it; each site needs its own account and key.

A worked example

A club site's plugin stores its key in configuration, schedules a fixtures refresh three times a day and a standings refresh each evening, and keeps both in options with timestamps. Editors place a fixtures block on the home page and a table block on the league page. A system cron triggers WordPress cron every few minutes. When the club wins on Saturday, the evening refresh updates the table and a purge hook clears the cached page.

Multisite and agencies

Agencies managing several club sites on a multisite network should give each site its own key, so usage and access are tracked separately and one site's problem does not affect the others. Store keys per site in configuration, and build the plugin so a missing key disables only that site's blocks with a clear notice to administrators.

Share the refresh code across sites but keep caches separate. A league table for one club's competition should never appear on another club's site because of a shared transient name.

Accessibility and mobile

Many WordPress themes squeeze tables on mobile. Wrap standings in a horizontally scrollable container with a visible caption, keep position, team and points columns visible, and test with a screen reader. Fixture lists read best as simple lists with date headings in the reader's chosen zone.

Before you launch

Check that the key never appears in page source, that pages still render when the API is unreachable, that the last-updated line is accurate, and that editors cannot type raw identifiers. Run the site for a full match weekend in staging, watching the usage page in your dashboard, before switching the blocks on publicly.

Choosing hosting

Shared hosts sometimes block outbound requests or limit cron. Confirm your host allows server-side HTTPS calls and scheduled tasks before building. If not, a small external worker can refresh the data and push it into WordPress through an authenticated REST endpoint you control, keeping the key off the WordPress host entirely.

Key takeaways

  • Store the key in configuration and fetch only from PHP.
  • Cache responses and refresh on a real schedule.
  • Render from cache with escaped, semantic output and image fallbacks.

Integration checklist

0/10 complete
Progress is saved in this browser only.

Frequently asked questions

Can I call the API from JavaScript in my theme?

No. That would expose your key. Fetch from PHP on the server and cache the result.

Should I use transients or options?

Transients for convenience, with the last good copy also stored as an option so it survives cache flushes.

Is WordPress cron reliable enough?

Trigger it from a real system scheduler where your host allows; otherwise refreshes depend on visitor traffic.

Can I import crests into the media library?

Do not assume image storage or redistribution is permitted. Review the data licence. Customer media routes require authenticated server-side requests; do not put an API key into an image URL, shortcode or browser script. Use a controlled server-side media delivery layer only within your agreed permissions.

Sources and references

Use these implementation references alongside this guide. Current endpoint parameters and account settings take precedence over illustrative examples.