Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sportmonks Football Api Laravel Package

pyaesoneaung/sportmonks-football-api

A Laravel package that wraps the SportMonks Football API, letting you fetch leagues, fixtures, teams, players, standings, and live scores through a clean, fluent interface. Simple setup, token handling, and structured responses make football data easy to integrate.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require pyaesoneaung/sportmonks-football-api
php artisan vendor:publish --tag=sportmonks-football-api-config

Add your token to .env (the config key is api_token, sourced from SPORTMONKS_API_TOKEN):

SPORTMONKS_API_TOKEN=your-token-from-my.sportmonks.com

Get a token first at my.sportmonks.com — the free/trial plan only grants access to a subset of leagues (Scottish Premiership, Danish Superliga, etc.), so start testing against a league you actually have entitlement for or you'll spend an hour debugging "not found" responses that are really entitlement errors.

Where to look first, in this order:

  1. config/sportmonks-football-api.php — token, base URL, and any default include/timeout settings. This is the whole surface area.
  2. src/ — the package is a thin wrapper, so the client class is the documentation. Resource methods map 1:1 to API v3 paths (leagues, fixtures, teams, players, standings, livescores, seasons, venues, coaches, referees, transfers).
  3. The official docs at docs.sportmonks.com/football-api — because the wrapper adds almost nothing on top, so every question about include / filter / select syntax is answered there, not in the README.

First use case — a "what's on today" page. It exercises the two things you'll do constantly (filtering by date, and eager-loading relations):

use PyaeSoneAungRgn\SportmonksFootballApi\Facades\SportmonksFootballApi;

$fixtures = SportmonksFootballApi::fixtures()
    ->includes(['participants', 'league', 'scores'])
    ->filter('fixturesBetween', '2026-08-15', '2026-08-16')
    ->get();

foreach ($fixtures['data'] as $fixture) {
    // $fixture['participants'] contains home/away with meta.location = 'home'|'away'
}

The exact builder method names vary by version — if includes() isn't there, inspect the client class; it's small enough that a single grep for the resource name gets you the full signature. Treat the README as a starting point only.

Implementation Patterns

Wrap it, don't sprinkle it. With 0 dependents and 26 stars, this package is a convenience, not an abstraction. Put it behind your own FootballRepository / FootballService so that (a) you can swap to raw Http:: calls or the official SDK later, and (b) you can normalize the array shapes before they hit your views.

final class FixtureRepository
{
    public function between(CarbonImmutable $from, CarbonImmutable $to): Collection
    {
        return Cache::remember("fixtures:{$from->toDateString()}", 600, function () use ($from, $to) {
            $response = SportmonksFootballApi::fixtures()
                ->includes(['participants', 'league', 'state'])
                ->filter('fixturesBetween', $from->toDateString(), $to->toDateString())
                ->get();

            return collect($response['data'])->map(fn (array $f) => FixtureData::fromApi($f));
        });
    }
}

Always send a narrow select and an explicit include. v3 returns deeply nested payloads by default; a fixtures list with participants, scores, events, and lineups included can be hundreds of KB per request. Request only the fields the page renders. This is the single biggest lever on both response time and how fast you burn through quotas.

Separate your caching strategy by resource volatility. Static-ish data (leagues, seasons, venues, teams) caches for hours or days. fixtures for a specific date caches until kickoff. livescores should not be cached at all — it's the one endpoint that's explicitly real-time, and stale in-play scores are worse than no scores.

Use scheduled jobs as the ingestion path, not request handlers. For anything involving livescores or standings, poll on a schedule and persist to your own Eloquent tables:

// routes/console.php
Schedule::command('football:sync-livescores')
    ->everyMinute()
    ->between('12:00', '23:59')
    ->withoutOverlapping();

Only poll during match windows in the timezones your users care about. Live polling burns quota at a rate that will silently exhaust mid-month on cheap plans.

Map the raw arrays into DTOs immediately. Sportmonks v3 uses data, include, and pagination style envelope keys, and includes come back as both embedded under a relation key and in a relations array on some resources — a DTO layer (FixtureData::fromApi(array $raw)) absorbs that inconsistency and gives you typed properties, CarbonImmutable cast dates, and a single place to fix things when the API changes.

Test with Http::fake(). Because the wrapper sits on Laravel's HTTP client, you can intercept without touching the package:

Http::fake([
    'api.sportmonks.com/v3/football/fixtures*' => Http::response(
        json_decode(file_get_contents(base_path('tests/fixtures/fixtures.json')), true)
    ),
]);

Keep a handful of real captured responses in tests/fixtures/ — this is how you protect yourself against the package changing or being abandoned. Do not hit the live API in CI; a test suite that calls the real endpoints will exhaust quota and flake.

Pagination pattern. Use page[number] / page[size] for bulk imports (all players in a league, all fixtures in a season), and drive it off the response's pagination metadata rather than assuming a page count. Bulk imports belong in queued jobs with a RateLimited middleware job guard, not in a controller.

Gotchas and Tips

  • The token travels as a query parameter. Sportmonks v3 authenticates with ?api_token=..., so the token appears in URLs that end up in Laravel's logs, Telescope, and exception traces. Immediately add sanitization: Telescope::hideRequestParameters(['api_token']), Telescope::hideResponseParameters(['*token*']), and a Str::mask() in any custom request logging. This is the most common way people leak their key with this API.

  • Free/trial plans return confusing errors for unentitled leagues. Often a 404 or an empty data array rather than a clear 403. When an endpoint "returns nothing," test the same call with a known-good trial league before blaming your code.

  • include syntax is semicolon-separated, not comma. Nested relations use dots (fixtures.participants), and a typo'd include is silently ignored rather than erroring — you just get no relation back. Validate your include list against the docs for that specific endpoint; not every relation is includable from every resource.

  • Filter names are camelCase and endpoint-specific (fixturesBetween, latest, today, inplay). There is no generic where — you're passing a filter name the API must recognize. Build a constants class for filter names you use, because a typo returns unfiltered results, not an error.

  • v3 has no rate-limit headers you can rely on across plans. Assume a per-plan request cap and enforce your own ceiling — a Redis token bucket or Laravel's RateLimiter around the repository calls. A 429 with no retry logic will bubble up as a broken page.

  • The package has no built-in retry or backoff. For production calls, wrap the client in your own retry:

    return retry(3, fn () => SportmonksFootballApi::fixtures()->...->get(), 500, fn ($e) => $e instanceof ConnectionException);
    

    Only retry connection/timeout errors — retrying a 429 immediately makes it worse.

  • Dates are UTC. starting_at comes back in UTC; cast and convert at the presentation layer, and be careful with Carbon::parse($str) picking up your app timezone. Use CarbonImmutable::parse($str, 'UTC')->setTimezone($user->timezone).

  • IDs are not globally unique across resource types. A fixture ID of 19140578 and a team ID of 19140578 are unrelated. Never key a single cache on a bare ID — prefix with the type (fixture:19140578).

  • scores is a list of typed entries, not a home/away pair. You have to read score['participant'] (home/away) and pick the right description (CURRENT, 2ND_HALF, FT) to get the scoreline you want. Filter on description === 'CURRENT' for live display.

  • Pin the version in composer.json. Last release is dated in the future relative to a normal cadence and the project is a single-maintainer wrapper; a minor bump can change a method signature. Use "^1.0" style constraints you've actually tested, and commit composer.lock.

  • Escape hatch: just use Http:: directly. If the package is missing an endpoint (or a v3 addition lands before the package updates), construct the request yourself against config('sportmonks-football-api.base_url'). You lose nothing but a method call, and it's the honest reason to keep this dependency behind a repository.

  • Debugging checklist for an unexpected empty response, in order: (1) dump the raw $response array, not a mapped DTO; (2) check pagination/meta for a message field; (3) verify plan entitlement with a trial league; (4) verify filter and include spelling; (5) confirm you're not hitting a cached empty result from your own Cache::remember.

  • Extension points are minimal — the config file and the facade are essentially it. If you need per-request timeouts or custom headers, the practical route is to publish the config and/or bind your own client into the container rather than waiting for upstream features.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc