PHPackages                             nietonchique/sofascore-api-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. nietonchique/sofascore-api-bundle

ActiveSymfony-bundle[API Development](/categories/api)

nietonchique/sofascore-api-bundle
=================================

Symfony bundle and standalone PHP client for the unofficial SofaScore API: matches, players, teams, tournaments and live per-sport statistics, with a pluggable HTTP / headless-Chrome transport.

v2.4.1(1mo ago)0247MITPHPPHP &gt;=8.4CI passing

Since Jun 13Pushed 1mo agoCompare

[ Source](https://github.com/nietonchique/Sofascore-API-Bundle)[ Packagist](https://packagist.org/packages/nietonchique/sofascore-api-bundle)[ RSS](/packages/nietonchique-sofascore-api-bundle/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (19)Versions (10)Used By (0)

SofaScore API Bundle
====================

[](#sofascore-api-bundle)

[![CI](https://github.com/nietonchique/Sofascore-API-Bundle/actions/workflows/ci.yml/badge.svg)](https://github.com/nietonchique/Sofascore-API-Bundle/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/efc82f0396d26ffe266cc0ebd681cedfe2eabc102a7165fa4a394f20ad1c815d/68747470733a2f2f636f6465636f762e696f2f67682f6e6965746f6e6368697175652f536f666173636f72652d4150492d42756e646c652f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/nietonchique/Sofascore-API-Bundle)[![Latest Version](https://camo.githubusercontent.com/d244f3d4d920fb9c830c1a781cc351c105138d8e8a0a67cac7c4e2525ad263f7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e6965746f6e6368697175652f736f666173636f72652d6170692d62756e646c652e737667)](https://packagist.org/packages/nietonchique/sofascore-api-bundle)[![PHP](https://camo.githubusercontent.com/ad80d7cd050c771f7fd6931100053e70795c3bc47826a61c51adde3cb608f8bb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e342532422d3737376262343f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://camo.githubusercontent.com/ad80d7cd050c771f7fd6931100053e70795c3bc47826a61c51adde3cb608f8bb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e342532422d3737376262343f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)[![Symfony](https://camo.githubusercontent.com/029babe1877b30127823cca997efd987b7a8e3ceb0cd835916734d01cdc6da21/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53796d666f6e792d382d3030303030303f6c6f676f3d73796d666f6e79266c6f676f436f6c6f723d7768697465)](https://camo.githubusercontent.com/029babe1877b30127823cca997efd987b7a8e3ceb0cd835916734d01cdc6da21/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53796d666f6e792d382d3030303030303f6c6f676f3d73796d666f6e79266c6f676f436f6c6f723d7768697465)[![License](https://camo.githubusercontent.com/7667282bb42ea46da3b960dda2c8fdcea90f717a9f32925754a27b762c0a9ab8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6e6965746f6e6368697175652f736f666173636f72652d6170692d62756e646c652e737667)](LICENSE)

A Symfony bundle **and** standalone PHP client for the (unofficial) [SofaScore](https://www.sofascore.com)API — matches, players, teams, tournaments and live per-sport statistics.

It is a faithful PHP port of the Python [`sofascore-wrapper`](https://github.com/tommhe14/sofascore-wrapper)library, redesigned around a pluggable transport layer, typed DTOs for the core entities, and full PHPStan (max) / Deptrac / PHPUnit coverage.

> **Disclaimer.** This is an **unofficial** client and is not affiliated with, endorsed by, or supported by SofaScore. The API it talks to is undocumented and may change or block access at any time. Using it may violate SofaScore's Terms of Service — use at your own risk.

Requirements
------------

[](#requirements)

- PHP **8.4+**
- Symfony **8.0+** components (when used as a bundle)
- Optional: a Chromium/Chrome binary + [`chrome-php/chrome`](https://github.com/chrome-php/chrome)for the headless-browser transport (Cloudflare fallback)

Installation
------------

[](#installation)

```
composer require nietonchique/sofascore-api-bundle
```

In a Symfony application using Symfony Flex the bundle is registered automatically. Otherwise add it to `config/bundles.php`:

```
return [
    // ...
    Nietonchique\SofascoreApiBundle\SofascoreApiBundle::class => ['all' => true],
];
```

Usage
-----

[](#usage)

### Standalone (any PHP project)

[](#standalone-any-php-project)

```
use Nietonchique\SofascoreApiBundle\SofascoreClient;

$client = SofascoreClient::create(); // default HTTP transport

$results = $client->search('arsenal')->searchAll();
$event   = $client->match(12436870)->getMatch();   // Dto\Event
$h2h     = $client->match(12436870)->h2h();         // array
$team    = $client->team(42)->getTeam();            // Dto\Team
$games   = $client->basketball()->gamesByDate('basketball', '2026-01-15');
$events  = $client->match()->scheduledEventsByDate('football', '2026-06-28');
```

### In Symfony (dependency injection)

[](#in-symfony-dependency-injection)

`SofascoreClient` and every endpoint group are autowired services:

```
use Nietonchique\SofascoreApiBundle\SofascoreClient;

final class ScoresController
{
    public function __construct(private readonly SofascoreClient $sofascore)
    {
    }

    public function live(): array
    {
        return $this->sofascore->match()->liveGames();
    }
}
```

Endpoint groups
---------------

[](#endpoint-groups)

Access each group via the client factory methods:

MethodGroupBound argument`search(string $q, int $page = 0)``Search`search term + page`match(?int $matchId = null)``MatchEndpoint`match (event) id`player(int $playerId)``Player`player id`playerSearch(string $query)``PlayerSearch`query`team(int $teamId)``Team`team id`league(int $leagueId)``League`unique-tournament id`manager(int $managerId)``Manager`manager id`transfers()``Transfers`—`news()``News`—`userData()``UserData`—`flag(string $flagCode)``Flag`country code`americanFootball()` / `baseball()` / `basketball()` / `cricket()` / `esports()` / `iceHockey()` / `mma()` / `motorsport()` / `rugby()` / `tennis()`per-sport groups—### Return types

[](#return-types)

The API surface is large and SofaScore changes response fields without notice, so the return style is intentionally hybrid and predictable:

- The **primary entity-detail getters** return typed DTOs: `MatchEndpoint::getMatch(): Event`, `Player::getPlayer(): Player`, `Team::getTeam(): Team`, `League::getLeague(): Tournament`. Every DTO keeps the full original payload accessible via `->raw` / `->toArray()`.
- **Every other method** returns a decoded `array` (the raw JSON), exactly as the Python library does.

Translations
------------

[](#translations)

Many SofaScore entities ship an embedded `fieldTranslations` dictionary. The typed DTOs expose it as a `?FieldTranslations` object:

```
$event = $client->match(12436870)->getMatch();
$team  = $event->homeTeam;

// Full translation maps
$team?->fieldTranslations?->name;       // ['ru' => 'Интер', 'sr' => 'Интер', ...]
$team?->fieldTranslations?->shortName;  // ['ru' => 'Инт', ...]

// Convenience accessor
$team?->fieldTranslations?->nameIn(LanguageCode::RU);        // 'Интер'
$team?->fieldTranslations?->nameIn(LanguageCode::SR);        // 'Интер'
$team?->fieldTranslations?->nameIn('xx', 'fallback');        // 'fallback'
```

`LanguageCode` is a convenience catalogue of known SofaScore locale codes (including the base set `en`, `ru`, `sr` and the codes observed in real responses). You can still pass any arbitrary string to `nameIn()` / `shortNameIn()`.

For methods that return raw arrays (e.g. `match()->gamesByDate()` / `match()->scheduledEventsByDate()`), the `fieldTranslations` key is preserved in the response unchanged.

Error handling
--------------

[](#error-handling)

Every exception thrown by the bundle implements `SofascoreExceptionInterface`:

ExceptionThrown when`ApiException`any non-2xx / undecodable response — base class, exposes `getStatusCode()` and `getUrl()``ApiBlockedException` (extends `ApiException`)HTTP 403 (Cloudflare); triggers the chain fallback`NotFoundException` (extends `ApiException`)HTTP 404 (unknown entity)`InvalidArgumentException`invalid argument, e.g. an unknown sport slug```
use Nietonchique\SofascoreApiBundle\Exception\ApiBlockedException;
use Nietonchique\SofascoreApiBundle\Exception\NotFoundException;
use Nietonchique\SofascoreApiBundle\Exception\SofascoreExceptionInterface;

try {
    $event = $client->match(12436870)->getMatch();
} catch (NotFoundException) {
    // no such match
} catch (ApiBlockedException $e) {
    // Cloudflare blocked this IP — configure a proxy (see below)
} catch (SofascoreExceptionInterface $e) {
    // any other error from the bundle: $e->getMessage()
}
```

Transports &amp; Cloudflare (403)
---------------------------------

[](#transports--cloudflare-403)

SofaScore sits behind Cloudflare. The bundle ships three transports behind a single `TransportInterface`:

- **`http`** — `HttpClientTransport`, a plain Symfony HTTP client with a realistic browser header set. Fast, no external dependencies.
- **`chrome`** — `ChromeTransport`, drives a headless Chromium via `chrome-php/chrome` (no Node.js). Warms up on the site root to obtain a Cloudflare clearance cookie before calling the API.
- **`chain`** (default) — tries HTTP first and falls back to Chrome on a 403.

> **The `X-Requested-With` header is required.** SofaScore answers every API request that lacks it with a Cloudflare `403 "challenge"`. `HttpClientTransport`sends it automatically (the value is not validated — a random per-instance hex token is used to mimic the site's own XHR token), so the default `http`transport reaches the API directly, no browser needed.
>
> **Some IPs are geo/reputation-blocked** (e.g. requests originating from Russia, or certain datacenter ranges) and get a `403` regardless of headers. Route through a clean exit with the `http.proxy` option — any SOCKS5/HTTP proxy works:
>
> ```
> sofascore_api:
>     http:
>         proxy: 'socks5h://127.0.0.1:1080'
> ```
>
>
>
> When still blocked, the transport raises `ApiBlockedException` rather than returning bogus data, and `ChainTransport` falls back to the Chrome transport.

### Configuration (bundle)

[](#configuration-bundle)

All decorators are **opt-in and disabled by default**:

```
# config/packages/sofascore_api.yaml
sofascore_api:
    transport: chain          # http | chrome | chain
    http:
        timeout: 10.0
        crypto_method: 768                # min TLS version; 768 = TLS 1.3 (default — Cloudflare 403s older handshakes)
        proxy: null                       # 'socks5h://127.0.0.1:1080'
        user_agent: null                  # override the default browser UA
        x_requested_with: null            # override the required header (default: random token)
        headers: {}                       # any extra headers
    chrome:
        binary: google-chrome-stable
        headless: true
        timeout_ms: 30000
        warmup_url: 'https://www.sofascore.com/'  # null to disable
        proxy: null                       # 'socks5://127.0.0.1:1080'
    retry:
        enabled: false
        max_retries: 3
        delay_ms: 1000
    cache:                    # PSR-6 response cache
        enabled: false
        pool: cache.app
        ttl: 300
    rate_limit:
        enabled: false
        limit: 60
        interval: '1 minute'
    logging:
        enabled: false
        service: logger
```

Quality
-------

[](#quality)

```
composer test       # PHPUnit (network tests excluded by default)
composer stan       # PHPStan (level max)
composer cs-check   # php-cs-fixer (@PSR12 + @Symfony), dry run
composer deptrac    # architecture/layer rules
composer qa         # all of the above
```

Run the live integration tests (they skip automatically when Cloudflare blocks the current IP):

```
vendor/bin/phpunit --group network
```

Development
-----------

[](#development)

A bundle is a library — "developing" it means editing code and running the test suite (there is no app server to start). Use the host PHP (8.4+) directly:

```
composer install
composer qa        # cs-fixer + phpstan + deptrac + phpunit
```

…or run everything in Docker (no PHP needed on the host; the container runs as your user, so it leaves no root-owned files):

```
make install       # PHP 8.5 by default — `make PHP=8.4 qa` to pick a version
make qa
make test
```

### Using the bundle in a Dockerized app

[](#using-the-bundle-in-a-dockerized-app)

The bundle is pure PHP; a minimal consumer image needs only PHP + ext-curl:

```
FROM php:8.5-cli
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY . .
RUN composer install --no-dev --prefer-dist --no-progress
```

For the headless-Chrome fallback, also install a Chromium binary and the optional dependency (`composer require chrome-php/chrome`, then `chrome.binary: chromium`):

```
RUN apt-get update && apt-get install -y --no-install-recommends chromium \
 && rm -rf /var/lib/apt/lists/*
```

> On a server/cloud the container's IP is a datacenter IP that Cloudflare blocks — route through a proxy (`http.proxy`) to a clean residential/mobile exit.

Troubleshooting
---------------

[](#troubleshooting)

- **Every request throws `ApiBlockedException` (403).** Three independent causes:
    1. the `X-Requested-With` header is missing — it is sent automatically, so this only happens if you overrode `http.headers` and dropped it;
    2. the exit IP is geo/reputation-blocked (e.g. Russia, some datacenter ranges). Set `http.proxy` (and `chrome.proxy`) to a clean exit;
    3. an older TLS handshake — Cloudflare fingerprints it (seen from inside containers even with a clean IP + header). The client defaults to TLS 1.3, which clears it; if you lowered `http.crypto_method`, raise it back.
- **`transport: chrome` fails with "class not found".** Install the optional dependency and make sure a Chromium binary is available: `composer require chrome-php/chrome` and set `chrome.binary`.
- **`rate_limit` / `cache` enabled but the container errors.** Install `symfony/rate-limiter`, and point `cache.pool` at a PSR-6 pool (`cache.app`ships with FrameworkBundle).
- **Headless Chrome still gets 403.** SofaScore's Cloudflare challenge is hard for headless automation; use the `http` transport with a clean residential/mobile proxy instead — it only needs the `X-Requested-With` header, which is sent for you.

Credits
-------

[](#credits)

- Ported from [`tommhe14/sofascore-wrapper`](https://github.com/tommhe14/sofascore-wrapper) (Python, MIT).

License
-------

[](#license)

[MIT](LICENSE) © Aleksandr Ryzhkov

###  Health Score

46

—

FairBetter than 92% of packages

Maintenance94

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

9

Last Release

30d ago

Major Versions

v1.1.0 → v2.0.02026-06-13

PHP version history (2 changes)v1.0.0PHP &gt;=8.2

v2.0.0PHP &gt;=8.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/8806554?v=4)[Aleksandr Ryzhkov](/maintainers/nietonchique)[@nietonchique](https://github.com/nietonchique)

---

Top Contributors

[![nietonchique](https://avatars.githubusercontent.com/u/8806554?v=4)](https://github.com/nietonchique "nietonchique (13 commits)")

---

Tags

apiclientSymfony Bundlesportsfootballsoccersofascore

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/nietonchique-sofascore-api-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/nietonchique-sofascore-api-bundle/health.svg)](https://phpackages.com/packages/nietonchique-sofascore-api-bundle)
```

###  Alternatives

[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M605](/packages/shopware-core)[shopware/platform

The Shopware e-commerce core

3.4k1.5M3](/packages/shopware-platform)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M215](/packages/sulu-sulu)[contao/core-bundle

Contao Open Source CMS

1231.6M2.9k](/packages/contao-core-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
