Build a league table widget
A step-by-step plan for an embeddable, accessible league table widget fed from a server-side cache.
FootballAPI.net editorial team
Written and reviewed by the organisation
Published 30 Oct 2026Updated 1 Nov 20265 min read

A league table widget is one of the most requested football features, and one of the best first projects with a football API. It uses a single feed, changes predictably and is easy to verify by eye. Done well, it can sit on a home page, a sidebar or a partner site and stay correct all season.
This tutorial describes the architecture and the rendering decisions rather than one framework's syntax, so you can apply it in React, a server-rendered template or plain HTML. It assumes you have a key and that access to the standings feed has been granted for your account.
Architecture
The widget has three parts. A server job fetches standings for your chosen league identifier and season, validates the response and stores it with a fetch time. A small endpoint on your server returns the stored table as compact JSON or pre-rendered HTML. The widget itself, running in the browser, requests that endpoint and renders the result.
The browser never sees your API key, and every widget view, on every page, reads the same cached data. That keeps request volume flat no matter how popular the widget becomes.
If you embed the widget on other sites, serve the endpoint with appropriate cache headers and cross-origin rules, and make sure the terms allow display on those sites before doing so.
- Server job fetches and stores
- Your endpoint serves the cached table
- The widget renders, never holding the key
Fetching standings
Request the standings feed for the league identifier and season. Use the supplied position for ordering rather than sorting yourself, because competitions use different tie-breakers. Store each row with team identifier, name, played, won, drawn, lost, goals for and against, goal difference and points, plus any zone description.
Schedule the job daily, with an extra run after the last match on match days. Keep the previous table if a refresh fails.
Choosing columns
A compact widget needs only position, team, played, goal difference and points. A full table adds wins, draws, losses and goals. Decide based on where the widget sits: sidebars want compact, league pages want full. Offer both through a size option rather than building two widgets.
Use a tabular-figure font for numbers so columns align. Right-align numeric columns and left-align team names.
Crests
Add crests through FootballAPI.net's media route using each team's identifier. Keep them small, set explicit dimensions, and lazy load in long tables. Provide a text fallback so a failed image leaves a neat gap rather than a broken icon.
Remember that league logos are not offered, so the widget header should use the competition name in text.
Accessibility
Render a real table with a caption naming the competition and season, header cells with scope attributes, and team names as links to team pages. Indicate zones with a text label or an accessible icon, not colour alone. Ensure the widget is navigable by keyboard and that focus styles are visible.
On small screens, keep position, team and points visible and allow horizontal scrolling for the rest, with a visible hint.
- Caption, header cells and scope
- Zones not by colour alone
- Keyboard and screen reader tested
Empty, stale and error states
Before a season starts, the table may be empty. Show a short message that the season has not begun. If the stored table is older than expected, show its fetch time prominently. If your endpoint fails, show a compact error with a retry button rather than a blank box.
Use a skeleton placeholder while loading, sized to the expected table, so the page does not jump when data arrives.
Highlighting a team
Club sites often want their own team highlighted. Accept a team identifier as an option and apply a subtle background to that row, with an accessible label such as your team. Do not rely on name matching, which fails after renames.
Performance
Pre-render the table HTML on the server if possible. It loads instantly and works without JavaScript. Hydrate only the interactive parts, such as a size toggle. Keep the widget's script small, since it may load on every page of the site.
Testing
Test with a full table, an empty pre-season table, a grouped competition with several tables, a team with a points deduction and a slow endpoint. Check on a narrow phone and a wide desktop. Compare the rendered positions with the supplied ones after every match day for the first few weeks.
A worked example
For league 1204, the server job runs each evening and after the final match on Saturdays. It stores twenty rows keyed on team identifier with the fetch time. The endpoint returns pre-rendered HTML for the compact size and JSON for the full size. A club site embeds the compact widget in its sidebar with its own team identifier highlighted, and the league page uses the full version with home and away splits behind a toggle.
When a refresh fails on a Saturday evening, the widget keeps showing the afternoon's table with its fetch time, and the next run corrects it. Nobody sees an empty box, and request volume stays at a handful of calls per day however many pages show the widget.
After a month, the team compares rendered positions with official tables each Monday and finds none out of order, which is exactly the confidence a widget like this needs before partners embed it.
Embedding on partner sites
If partners embed the widget, provide a single script tag or an iframe with a documented size, and version the script so updates do not break their layouts. Confirm the display terms cover partner sites first, and include a small attribution link back to your own site. Keep the endpoint rate-limited so a misbehaving partner page cannot overload your server.
Key takeaways
- Fetch standings on the server and serve the widget from cache.
- Trust supplied positions and build an accessible table.
- Design empty, stale and error states from the start.
Request-budget planner
Estimate calls from one shared server-side poller. Browser visitors should read your cache, not the API.
- Calls per day
- 1,800
- Calls per month
- 54,000
- Average rate
- 0.05 req/s
An estimate only. The average rate applies during active hours, not to bursts: the account safety ceiling is 100 requests per second. Inputs are bounded to 1–86,400 seconds, 0–24 hours, 0–100 endpoints and 0–31 days. Include other workers and detail requests in your forecast. Daily included successful requests reset at midnight UTC without rollover; metered extras cost €0.002 each. Successful API cache hits count too. See the current daily allowances.
Frequently asked questions
Can the widget call the API directly?
No. It should call your own endpoint, which serves cached standings fetched server-side.
How do I show crests?
Use the media route with each team's identifier, with explicit dimensions and a text fallback.
What about grouped competitions?
Render one table per group from the response, each with its own caption.
How often should the table refresh?
Daily, plus once after the last match on match days.
Sources and references
- Find current standings inputs and response documentation
- Review safe server-side client examples
- FootballAPI.net coverage notes
Use these implementation references alongside this guide. Current endpoint parameters and account settings take precedence over illustrative examples.
Related guides
All guidesFootball standings API: building reliable league tables
How standings feeds represent league tables, groups and home and away records, and how to render them accurately.
Adding football data to a WordPress site
How to show fixtures and tables on WordPress safely, using server-side requests, transients and scheduled refreshes.
Team crests, player photos and venue images
How FootballAPI.net serves crests, player photos and stadium images through its own media route, and how to use them well.