# Flashscore Standings Scraper (`romy/flashscore-standings-scraper`) Actor

Fetches league/tournament standings (points tables) from FlashScore for one or more competitions in a single run: rank, played, won, drawn, lost, goals, points, with overall/home/away/form table types. No login or API key needed. Derived from romy/flashscore-all-in-one-api.

- **URL**: https://apify.com/romy/flashscore-standings-scraper.md
- **Developed by:** [Romy](https://apify.com/romy) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.10 / 1,000 standing rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does FlashScore Standings Scraper do?

**FlashScore Standings Scraper** takes one or more FlashScore tournament/stage id pairs and pushes the current league points table (standings) for each, one dataset row per competition queried.

It talks directly to the same internal API the official [FlashScore](https://www.flashscore.com/) Android app uses (`global.lsapp.eu`), reverse-engineered via Frida SSL-pinning bypass and live mitmproxy capture against a real device — the exact static headers and feed format the app itself sends, never guessed. No FlashScore account, no API key, no scraping setup. This is the standings slice of [FlashScore All-in-One API](https://apify.com/romy/flashscore-all-in-one-api), split out as its own focused, batch-run Actor.

### Why use FlashScore Standings Scraper?

- **Batch by design** — pass an array of competitions, get one dataset row per competition, all fetched in parallel
- **Real, confirmed field mapping** — rank, team, played, won, goals, points, and qualification zone were cross-checked against the rendered standings screen for real English Premier League data, not guessed from the raw feed
- **No lookup step needed in the common case** — `tournamentId` and `tournamentStageId` both come straight from the parent Actor's `/leagues` endpoint
- **Optional best-effort resolve** — if you only have a `tournamentStageId` (e.g. from a competition tree or search result), the Actor will try to resolve `tournamentId` for you before failing
- **Use cases:** standings dashboards, league-table snapshots for multiple competitions in one run, feeding a downstream analytics pipeline

### Input

| Field                              | Type   | Description                                                                                                                                                                                   |
| ---------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `competitions`                     | array  | **Required, non-empty.** Each item: `{ tournamentId?, tournamentStageId, type? }`.                                                                                                            |
| `competitions[].tournamentId`      | string | FlashScore tournament id, e.g. `"SY30SsKF"` for Premier League. Get it from the parent Actor's [`/leagues`](https://apify.com/romy/flashscore-all-in-one-api) endpoint. Optional — see below. |
| `competitions[].tournamentStageId` | string | FlashScore tournament stage id, e.g. `"CfoA8Dmm"` for Premier League's Main stage. **Required.**                                                                                              |
| `competitions[].type`              | string | Standings table type: `OVERALL`, `HOME`, `AWAY`, or `FORM`. Defaults to `OVERALL`.                                                                                                            |

If `tournamentId` is omitted, the Actor first calls the parent's `getTournamentSeasons(tournamentStageId)` and tries to extract a `tournamentId` from the response as a best effort — that endpoint's return shape is not fully verified upstream, so this is a fallback, not the primary path. If it can't find one, the Actor throws a clear error asking you to supply `tournamentId` directly from `/leagues` instead.

Example:

```json
{
    "competitions": [
        { "tournamentId": "SY30SsKF", "tournamentStageId": "CfoA8Dmm" },
        { "tournamentStageId": "CfoA8Dmm", "type": "FORM" }
    ]
}
```

### Output

One dataset row per competition entry. Real output from a live run against English Premier League 2026/27 (`tournamentId: "SY30SsKF"`, `tournamentStageId: "CfoA8Dmm"`), trimmed to two rows:

```json
{
    "tournamentId": "SY30SsKF",
    "tournamentStageId": "CfoA8Dmm",
    "type": "OVERALL",
    "standings": [
        {
            "rank": 1,
            "teamId": "Wtn9Stg0",
            "teamName": "Man City",
            "teamLogo": "https://static.flashscore.com/res/image/data/UXcqj7HG-lQuhqN8N.png",
            "played": 2,
            "won": 2,
            "goals": "6:2",
            "points": 6,
            "qualificationZone": "q1"
        },
        {
            "rank": 2,
            "teamId": "S66R0t75",
            "teamName": "Hull",
            "teamLogo": "https://static.flashscore.com/res/image/data/jL2Nk4Fa-bJFnYpTt.png",
            "played": 2,
            "won": 2,
            "goals": "3:0",
            "points": 6,
            "qualificationZone": "q1"
        }
    ]
}
```

`goals` is kept as the raw upstream `"for:against"` string (e.g. `"6:2"`) rather than split into two fields. `qualificationZone` (e.g. `"q1"` for a Champions League spot) is only present on rows that actually have one.

### Known limitations

- Top scorers, draws/brackets, results, and fixtures are not part of this Actor — they're covered by the parent [FlashScore All-in-One API](https://apify.com/romy/flashscore-all-in-one-api) directly, or by future spin-off Actors.
- `getTournamentSeasons()`'s response shape is unverified upstream — the `tournamentId`-omitted resolve path is best-effort, not guaranteed. Supplying `tournamentId` directly from `/leagues` is the reliable path.
- Only the four confirmed table types (`OVERALL`, `HOME`, `AWAY`, `FORM`) are supported — there is no `4` variant; it was never a real value in the app's own enum.

### Pricing

Pay-per-event, `standing` — charged once per standings row returned (one team/entry in a table). FREE tier: $0.004 per event. See the Actor's Pricing tab for current rates across all plan tiers.

# Actor input Schema

## `competitions` (type: `array`):

Array of competitions to fetch standings for. Each item: { tournamentId?, tournamentStageId, type? }. tournamentId and tournamentStageId normally both come directly from the parent Actor's /leagues endpoint (https://apify.com/romy/flashscore-all-in-one-api) - no extra lookup needed. If tournamentId is omitted, it is resolved via a best-effort getTournamentSeasons(tournamentStageId) call first. type is one of OVERALL, HOME, AWAY, FORM (default OVERALL).

## Actor input object example

```json
{
  "competitions": [
    {
      "tournamentId": "SY30SsKF",
      "tournamentStageId": "CfoA8Dmm"
    }
  ]
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "competitions": [
        {
            "tournamentId": "SY30SsKF",
            "tournamentStageId": "CfoA8Dmm"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("romy/flashscore-standings-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "competitions": [{
            "tournamentId": "SY30SsKF",
            "tournamentStageId": "CfoA8Dmm",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("romy/flashscore-standings-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "competitions": [
    {
      "tournamentId": "SY30SsKF",
      "tournamentStageId": "CfoA8Dmm"
    }
  ]
}' |
apify call romy/flashscore-standings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,romy/flashscore-standings-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/pQ22GiSSRSqnCUpKA/builds/2EjU17yVmujDCP6GC/openapi.json
