# Liquipedia Scraper - Esports Tournaments & Teams (`clintsa/liquipedia-scraper`) Actor

Extract structured esports tournaments, teams, players, transfers, brackets and match schedules from Liquipedia across 80+ game wikis. Prize pools, rosters, final standings and results as typed JSON, not raw HTML. Reads only the official MediaWiki API. $0.002 per record plus $0.001 per run.

- **URL**: https://apify.com/clintsa/liquipedia-scraper.md
- **Developed by:** [Andy Besos](https://apify.com/clintsa) (community)
- **Categories:** News, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 esports records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

This Liquipedia Scraper turns [Liquipedia](https://liquipedia.net) esports wiki pages into clean, typed JSON. Point it at tournaments, teams, or players on any of 80+ game wikis — Dota 2, Counter-Strike, League of Legends, Valorant, Rocket League, Overwatch, Apex Legends, Rainbow Six, StarCraft II, Mobile Legends and the rest — and get back prize pools as numbers, dates as ISO dates, rosters as arrays, **final standings showing which team finished in which place**, **roster transfers**, **playoff brackets**, and the **upcoming match schedule with exact UTC kickoff times**. Not raw HTML you still have to parse.

You do not need a Liquipedia account, an API key, a browser, or a proxy. The Actor reads only Liquipedia's official MediaWiki API, stays inside the published rate limits, and attaches the CC-BY-SA 3.0 attribution you need to republish the data.

### Why use this Liquipedia scraper?

- **Structured fields, not an HTML dump.** `prizePoolUsd` is a number, `startDate` and `endDate` are `YYYY-MM-DD` strings, `series`, `organizers`, `sponsors` and `venues` are arrays. Other Liquipedia scrapers hand you the rendered page body and leave the infobox parsing to you.
- **Tournaments, players, and teams in one Actor**, on one schema. A tournament record carries its full `participants` array — every team with its `players`, `coaches`, and the `qualifier` path it came through — plus `prizeDistribution` per placement. A player record carries `teamHistory` as a dated timeline, plus `externalIds` for Steam, FACEIT, ESEA, ESL and GamersClub. A team record carries staff, region, founding dates, and socials.
- **Final standings and match results.** Turn on standings and each tournament gains a `standings` array — place, participant, prize money, circuit points — including shared placements such as `5-6`. Turn on matches and you also get every match on the page with opponents, date, best-of, and a derived score.
- **Roster transfers, as a feed or as history.** Every move comes back as its own row: the date, the players involved with their countries, the team left, the team joined, the roles on each side, and the sources Liquipedia cites. Take the live feed for a daily transfer watch, or point `pages` at the quarterly archives — `Transfers/2025/1st Quarter` and its siblings back to 2006 — and pull hundreds of historical moves per page at full speed.
- **The match schedule, with real kickoff times.** The ticker is the only place Liquipedia publishes an exact instant, and this Actor returns it as UTC. Scheduled, live and just-finished matches, each with both opponents, the tournament, the best-of, and the score once it exists.
- **Brackets and head-to-head.** Playoff brackets come back with their layout, every slot, and the round names the editors wrote — even on the wikis whose results live in the database, where the shape of the bracket is all the page holds. Head-to-head records are derived from the matches you scraped, one object per side with the series and maps it won, plus which tournaments they met in.
- **Career results and prize money.** Turn on career results and a player or team profile gains its total winnings, recent placements with prize money and opponents, and individual awards.
- **Change monitoring built in.** Every record is tagged `new`, `updated`, `unchanged`, or `removed`, and updated records list exactly which fields moved in `changedFields`. Schedule it and you have a roster-move and prize-pool watcher instead of a one-off export.
- **Whole categories in one run.** Give it `Tournaments`, `Players`, or `2024 Tournaments` and it enumerates the category and scrapes every article in it, following continuation automatically.
- **It accepts the input you already have.** Full URLs, `wiki:Page Title`, `wiki/Page Title`, bare titles, underscores, URL encoding, and `index.php?title=` links all normalize to the same page. Liquipedia redirects are resolved, so `The International 2024` finds `The International/2024` on its own.
- **Honest nulls.** A match that has not started reports no score rather than the ticker's placeholder `0 : 0`, and some Liquipedia values — Valve's live-updated prize pools, for example — are computed by the wiki at render time and simply are not present in the page source. This Actor returns `null` and names the field in `unresolvedFields` instead of inventing a number.

Reliability comes from the access path. Liquipedia's [API Terms of Use](https://liquipedia.net/api-terms-of-use) state that automated access to rendered HTML pages is not permitted, and those pages sit behind a bot challenge. This Actor never touches them. It uses the MediaWiki API with an identifying User-Agent, gzip, a reused connection, and one request every 2.1 seconds — the documented limit. Requests are batched 50 pages at a time, temporary errors retry with backoff that honours `Retry-After`, and one failing wiki never aborts the others: the run fails only if every wiki fails.

### Pricing

This Actor uses the pay-per-event model. You pay **$0.001 per run plus $0.002 per record** — that is **$2.00 per 1,000 records**. There are no separate platform usage, proxy, or compute charges.

| Records in a run | Total cost |
| ---: | ---: |
| 1 | $0.003 |
| 100 | $0.201 |
| 1,000 | $2.001 |
| 10,000 | $20.001 |

For comparison: in our **21 August 2026** review of every Liquipedia scraper returned by Apify Store search, the highest-traffic one charged **$0.016 per result** ($16.00 per 1,000) and returned unparsed HTML. Prices change — check the current figures before relying on this comparison.

### How to use this Actor

Set **wiki** to the game wiki you want, then either list **pages** or give a **category** or a **search** term.

The wiki is the first path segment of any Liquipedia URL: `https://liquipedia.net/dota2/...` is `dota2`, `https://liquipedia.net/counterstrike/...` is `counterstrike`.

Every one of these is an accepted **pages** entry:

- `ESL One/Birmingham/2024` — a page title on the selected wiki
- `https://liquipedia.net/dota2/ESL_One/Birmingham/2024` — a full page URL, which carries its own wiki
- `counterstrike:Natus Vincere` — a wiki prefix and a title
- `valorant/TenZ` — the same, with a slash
- `The International 2024` — a redirect, resolved automatically to `The International/2024`

Scrape a few specific pages across two wikis:

```json
{
  "wiki": "dota2",
  "pages": [
    "ESL One/Birmingham/2024",
    "https://liquipedia.net/counterstrike/Natus_Vincere",
    "valorant/TenZ"
  ],
  "maxPagesPerWiki": 0
}
```

Scrape every tournament on the Counter-Strike wiki, capped at 500 pages:

```json
{
  "wiki": "counterstrike",
  "category": "Tournaments",
  "entityTypes": ["tournament"],
  "maxPagesPerWiki": 500
}
```

Find and scrape pages by search term:

```json
{
  "wiki": "valorant",
  "search": "Champions 2024",
  "maxPagesPerWiki": 25
}
```

Because Liquipedia allows one API request every two seconds and this Actor fetches 50 pages per request, roughly 1,400 pages per minute is the practical ceiling. A 500-page category run takes well under a minute.

### What is in the page, and what is not

Liquipedia keeps two kinds of data. Most of it is written in the page source and comes back at full speed — 50 pages per API request. The rest Liquipedia assembles from its own database when a page renders, and reading that costs a heavier API call its terms limit to **one request per 30 seconds**. Every one of those is opt-in, so you decide what a run spends its time on.

| Option | What you get | Cost |
| --- | --- | --- |
| `includeMatches` | Matches on the page: opponents, date, best-of, derived score | Free |
| `includeBrackets` | Playoff bracket layout, every slot, round names | Free |
| `includeHeadToHead` | Derived head-to-head records between everyone who met | Free |
| Transfer archives in `pages` | Hundreds of historical transfers per page | Free |
| `includeStandings` | Final placement table with prize money and points | 30 s per tournament |
| `includeTransferFeed` | The wiki's latest roster moves | 30 s per wiki |
| `includeMatchTicker` | Upcoming, live and finished matches with UTC kickoff | 30 s per wiki |
| `includeResults` | Career winnings, placements and awards on a profile | 30 s per profile |

The Actor reads the platform's own run deadline and stops asking for rendered pages while it still has time to write what it already collected, so a run cannot time out mid-lookup and lose everything. Raise the run timeout when you want more of them: allow about 30 seconds each.

#### Final standings

```json
"standings": [
  { "place": "1",   "participant": "Team Falcons",  "usdPrize": 300000, "points": 6400 },
  { "place": "2",   "participant": "BetBoom Team",  "usdPrize": 175000, "points": 4800 },
  { "place": "5-6", "participant": "Team Liquid",   "usdPrize": 57500,  "points": 2240 },
  { "place": "5-6", "participant": "G2 x iG",       "usdPrize": 57500,  "points": 2240 }
]
```

Shared placements such as `5-6` are repeated for every team that spans them, so each row stands on its own.

#### Transfers

One record per move, from either source, on one schema:

```json
{
  "entityType": "transfer",
  "date": "2025-03-28",
  "dateIsExact": true,
  "players": [{ "name": "TA2000", "country": "kz" }, { "name": "lorenof", "country": "ua" }],
  "fromTeam": "Chimera Esports",
  "toTeam": "Virtus.pro",
  "toRole": null,
  "references": ["https://t.me/chimera_esports/616"],
  "transferSource": "wikitext"
}
```

A row that moves a whole roster stays one row, with every player in `players`. A missing side means what it says: `fromTeam: null` is a player joining from no team, `toTeam: null` is one becoming a free agent — never an invented team name. When Liquipedia only knows the month, `dateIsExact` is `false` and `dateRaw` keeps the original `2025-03-??`.

For a **daily transfer watch**, set `includeTransferFeed` and a stable `monitorId`. For **bulk history**, put the archives in `pages` instead — they are free and far larger:

```json
{
  "wiki": "counterstrike",
  "pages": ["Transfers/2025/1st Quarter", "Transfers/2024/4th Quarter"],
  "maxPagesPerWiki": 0
}
```

#### Match schedule

```json
{
  "entityType": "match",
  "scheduledAt": "2026-08-22T02:00:00.000Z",
  "finished": false,
  "opponents": [
    { "name": "TSpirit", "page": "Team Spirit", "score": null, "winner": false },
    { "name": "Liquid",  "page": "Team Liquid", "score": null, "winner": false }
  ],
  "score": null,
  "bestOf": 3,
  "tournament": "TI 2026 - Main Event",
  "tournamentPage": "The International/2026/Main Event"
}
```

An unplayed match reports `score: null`. The ticker prints `0 : 0` before kickoff, and passing that on as a result would put a fabricated draw in your dataset.

#### Brackets

```json
"brackets": [{
  "bracketId": "zgZcb3UKXx",
  "layout": "Bracket/4U4L2DSL1D",
  "source": "wikitext",
  "nodeCount": 10,
  "nodes": [
    { "key": "R2M1", "round": 2, "matchNumber": 1, "roundLabel": "Upper Bracket Semifinals",
      "match": { "opponents": ["team falcons", "tundra esports"], "score": [1, 2], "winner": "tundra esports" } }
  ]
}]
```

Round names come from the headings the editors wrote above each group. Empty slots are kept rather than dropped, so on wikis that store their results in the database you still get the shape of the bracket. A bracket embedded only as `{{ShowBracket}}` is listed with `source: "liquipediadb"` and no slots — that way "this event had no bracket" stays distinguishable from "the bracket is not in the page source".

#### Match results in the page source

`includeMatches` costs no extra requests, but how much is there depends on the wiki:

| Wiki | What you get | `scoreSource` |
| --- | --- | --- |
| Dota 2, Overwatch | Opponents, date, best-of, and a score derived from per-map winners | `map-winners` |
| Counter-Strike | Opponents, date, and a score derived from per-side round counts | `round-totals` |
| Valorant, League of Legends | Nothing — these wikis transclude matches from the database, so their pages carry only empty placeholders | — |

Every match names how its score was worked out in `scoreSource`, and `decidedMatchCount` tells you how many of a tournament's matches actually resolved to a winner. A match that cannot be decided returns `score: null` and `winner: null` rather than a guess. Drawn matches, which are normal in best-of-two group stages, also return no winner. Where the page carries no matches, `includeMatchTicker` still gives you the schedule and the finished results.

#### Head-to-head

`includeHeadToHead` adds one record per pair of opponents that met in the matches this run parsed:

```json
{
  "entityType": "headtohead",
  "name": "BetBoom Team vs Team Falcons",
  "opponents": [
    { "name": "BetBoom Team",  "seriesWon": 0, "mapsWon": 1 },
    { "name": "Team Falcons",  "seriesWon": 1, "mapsWon": 4 }
  ],
  "seriesPlayed": 2,
  "drawnSeries": 1,
  "firstMatchDate": "2024-04-22",
  "lastMatchDate": "2024-04-28",
  "tournaments": ["ESL One Birmingham 2024"],
  "sourcePages": ["ESL One/Birmingham/2024"],
  "derivedFrom": "matches on the pages scraped in this run"
}
```

A derived record has no page of its own, so `pageUrl` is null and `sourceUrls` lists every page that contributed to it — that is what you attribute under CC-BY-SA 3.0.

Liquipedia's own head-to-head pages are database queries the API does not expose, so these are computed from what you scraped — scrape a whole season and you get that season's head-to-head table. Every record says so in `derivedFrom` and lists the tournaments it covers.

### Monitor new, updated, and removed records

Run the Actor on a schedule with a stable **monitorId** and it compares each run against the previous one for that ID.

| `changeType` | Meaning |
| --- | --- |
| `new` | The page appeared in this selection for the first time. |
| `updated` | A field changed since the last run. `changedFields` names them. |
| `unchanged` | The page is still there and identical. |
| `removed` | A page that was present before is no longer in the selection. Emitted once. |

This is what makes roster moves, staff changes, and prize-pool updates visible: when a player signs elsewhere, their record comes back `updated` with `changedFields: ["currentTeam", "teamHistory"]`.

Watch a set of teams for roster changes every morning:

```json
{
  "wiki": "counterstrike",
  "pages": ["Natus Vincere", "G2 Esports", "Team Vitality", "FaZe Clan"],
  "maxPagesPerWiki": 0,
  "monitorId": "cs-orgs",
  "onlyChangesSince": "2026-08-01T00:00:00.000Z"
}
```

With `onlyChangesSince` set, the dataset holds only what actually moved, so a quiet day costs one record's worth of nothing. Keep `wiki`, `pages`, `entityTypes`, and `maxPagesPerWiki` stable across runs that share a monitor ID — they define the set being compared, so changing them makes pages look added or removed.

### API example

Run the Actor and get the records back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/clintsa~liquipedia-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "wiki": "dota2",
    "pages": ["ESL One/Birmingham/2024"],
    "maxPagesPerWiki": 1
  }'
```

### Input options

| Field | Type | Description |
| --- | --- | --- |
| `wiki` | string | **Required.** Liquipedia game wiki, such as `dota2` or `counterstrike`. Used for bare titles and for category and search discovery. |
| `pages` | array | Page URLs or titles to scrape. Leave empty when using `category` or `search`. |
| `category` | string | Enumerate a Liquipedia category on the selected wiki, such as `Tournaments`, `Teams`, or `Players`. |
| `search` | string | Full-text search on the selected wiki; matching pages are scraped in relevance order. |
| `entityTypes` | array | Keep only `tournament`, `player`, `team`, `transfer`, `match`, `headtohead`, or `other` records. Empty keeps everything. |
| `includeMatches` | boolean | Add a `matches` array to tournaments. Costs no extra requests; coverage varies by wiki. |
| `includeBrackets` | boolean | Add a `brackets` array with layout, slots and round names. Costs no extra requests. |
| `includeStandings` | boolean | Add a `standings` array with final placements. One extra API call per tournament, limited to one per 30 seconds. |
| `maxStandingsLookups` | integer | Cap on standings lookups per run. Default 8, which fits the 300-second default timeout. |
| `includeHeadToHead` | boolean | Add derived head-to-head records for every pair that met in this run's matches. Costs no extra requests. |
| `includeTransferFeed` | boolean | Add the wiki's latest roster moves as one record each. One 30-second call per wiki. |
| `transferFeedPage` | string | Page the transfer feed is read from. Default `Portal:Transfers`. |
| `includeMatchTicker` | boolean | Add upcoming, live and finished matches with UTC kickoff times. One 30-second call per wiki. |
| `matchTickerPage` | string | Page the match ticker is read from. Default `Liquipedia:Matches`. |
| `includeResults` | boolean | Add career winnings, placements and awards to player and team records. One 30-second call per profile. |
| `maxResultLookups` | integer | Cap on career-result lookups per run. Default 4. |
| `maxPagesPerWiki` | integer | Cap on pages fetched per wiki. `0` means no cap. |
| `includeWikitext` | boolean | Add the page's raw wikitext to each record as `wikitext`. |
| `contactEmail` | string | Optional contact address added to the API User-Agent, as Liquipedia's terms ask for. |
| `monitorId` | string | Namespace for change tracking. Same value means same monitor. |
| `onlyChangesSince` | string | ISO timestamp; return only records that changed since then. |
| `requestDelayMillis` | integer | Extra pacing. The 2,100 ms floor required by Liquipedia always applies. |
| `useApifyProxy` | boolean | Optional network fallback. Normally unnecessary. |
| `proxyConfiguration` | object | Proxy settings, used only when `useApifyProxy` is enabled. |
| `startUrls` | array | Compatibility alias merged into `pages`. |

### Output

One record per page. A tournament looks like this (abridged):

```json
{
  "entityType": "tournament",
  "itemId": "dota2:ESL One/Birmingham/2024",
  "wiki": "dota2",
  "pageTitle": "ESL One/Birmingham/2024",
  "name": "ESL One Birmingham 2024",
  "tickerName": "ESL One Birmingham",
  "series": ["ESL One", "ESL Pro Tour"],
  "organizers": ["ESL Gaming"],
  "sponsors": ["Intel Arc", "Predator", "DHL", "Qiddiya", "Monster Energy"],
  "eventType": "Offline",
  "tier": "1",
  "startDate": "2024-04-22",
  "endDate": "2024-04-28",
  "prizePoolUsd": 1000000,
  "teamCount": 12,
  "country": "United Kingdom",
  "city": "Birmingham",
  "venues": ["Resorts World Arena"],
  "patch": "7.35d",
  "prizeDistribution": [
    { "place": "1", "usdPrize": 300000, "points": 6400, "percentage": null, "computed": false },
    { "place": "5-6", "usdPrize": 57500, "points": 2240, "percentage": null, "computed": false }
  ],
  "participants": [
    {
      "team": "Gaimin Gladiators",
      "players": ["dyrachyo", "Quinn", "Ace", "tOfu", "Seleri"],
      "coaches": ["Cy-"],
      "qualifier": "EPT Leaderboard"
    }
  ],
  "participantCount": 12,
  "previousEditions": ["ESL One/Kuala Lumpur/2023", "DreamLeague/Season 22"],
  "unresolvedFields": [],
  "website": "https://www.esl-one.com/birmingham/",
  "socials": { "twitter": "ESLDota2", "twitch": "esl_dota2" },
  "pageUrl": "https://liquipedia.net/dota2/ESL_One/Birmingham/2024",
  "license": "CC-BY-SA 3.0",
  "changeType": "new",
  "scrapedAt": "2026-08-21T12:00:00.000Z"
}
```

Player records add `handle`, `realName`, `nativeName`, `birthDate`, `country`, `status`, `roles`, `currentTeam`, a dated `teamHistory`, and `externalIds` — plus `totalWinningsUsd`, `results` and `awards` when career results are enabled. Team records add `location`, `region`, `founders`, `ceo`, `managers`, `coaches`, `analysts`, and `inGameLeader`. Transfers, ticker matches and head-to-head pairings arrive as their own records, shown above. Five dataset views are provided: **Esports records**, **Tournaments**, **Transfers**, **Match schedule**, and **Changes**.

Every run also writes a `SUMMARY` record to the key-value store with the number of wikis requested and succeeded, pages fetched, records exported, a breakdown by record type, and the change counts, plus how many rendered lookups the run spent.

### Use cases

- Build an esports results and prize-money database across several games without writing a wikitext parser.
- Watch a set of organizations for roster moves and staff changes, and alert on `changedFields`.
- Track prize pools and tier assignments across a circuit to size sponsorship or betting markets.
- Populate a fantasy-esports or tipping product with tournament dates, participants, and rosters.
- Feed a research dataset on player careers using dated `teamHistory` timelines across 80+ titles.
- Enrich a CRM of teams and organizations with regions, staff, socials, and founding dates.
- Run a daily transfer watch across several games and alert on every new roster move.
- Drive a schedule or notification product from the match ticker's exact UTC kickoff times.
- Build head-to-head tables for a whole season by scraping its tournaments in one run.

### FAQ

**Do I need a Liquipedia account or API key?**
No. This Actor uses Liquipedia's public MediaWiki API, which needs no authentication. You do not need the separate LiquipediaDB API key either.

**Which games are supported?**
All of them. Any wiki hosted on liquipedia.net works by name, including Dota 2, Counter-Strike, League of Legends, Valorant, Rocket League, Overwatch, Apex Legends, Rainbow Six, StarCraft II, Mobile Legends, Call of Duty, Fortnite, PUBG, Halo, Smash, Chess and more than seventy others. A newly launched wiki works the day it appears.

**Is scraping Liquipedia allowed?**
Liquipedia publishes API Terms of Use that permit automated access through the MediaWiki API and forbid it against rendered HTML pages. This Actor only uses the API, sends an identifying User-Agent, and respects the one-request-per-two-seconds limit. Content is licensed CC-BY-SA 3.0, and every record carries `pageUrl`, `license`, and `licenseUrl` so you can attribute it correctly.

**Why is `prizePoolUsd` null for some tournaments?**
Two reasons, and in both the Actor tells you rather than guessing.

Some events publish their prize pool in a local currency only — LCK lists ₩375,000,000 and LEC €80,000, with no dollar figure. Those come back as `prizePoolLocal: 375000000` with `localCurrency: "krw"`, and `prizePoolUsd` stays null. Reading the local number as dollars would report a $375-million prize pool for a tournament that pays out a fraction of that, so the Actor never does it. Convert with your own exchange rate if you need one comparable column.

A few events, notably Valve's, compute the prize pool from a separate page or a live counter, so the number is not in the page source at all. The field name then appears in `unresolvedFields`, and affected `prizeDistribution` entries are flagged `computed: true` with their `percentage` filled in where published.

**How fast can it scrape?**
About 1,400 pages per minute. Liquipedia allows one request every two seconds, and the Actor asks for 50 pages per request.

**Can it get final standings and match results?**
Yes, both are optional fields. Enable `includeStandings` for the placement table — which team finished where, with prize money and points. Because Liquipedia renders that table from its own database, it needs a heavier API call that its terms limit to one request per 30 seconds, so use it on a shortlist rather than a whole category. Enable `includeMatches` for the matches on the page; that costs nothing extra, but Valorant and League of Legends pages carry no match data at all, and each match reports how its score was derived in `scoreSource`. For those wikis, `includeMatchTicker` gives you the schedule and results anyway.

**Can it track roster transfers?**
Yes, two ways. `includeTransferFeed` returns the wiki's latest moves as one record each — combine it with a stable `monitorId` and a schedule for a daily transfer watch. For history, put the quarterly archives such as `Transfers/2025/1st Quarter` into `pages`: those are read from the page source, so they are free, fast, and go back to 2006.

**Do I need the LiquipediaDB API key for any of this?**
No. Everything described here comes from the public MediaWiki API. LiquipediaDB is a separate, key-gated API with an hourly request quota; this Actor does not use it, so nothing you run here depends on a key of yours.

**How do I get only what changed?**
Set a stable `monitorId` and schedule the run. Add `onlyChangesSince` with an ISO timestamp to drop `unchanged` records from the dataset entirely.

### Limitations and responsible use

The optional fields that read rendered pages — standings, transfer feed, match ticker, career results — each cost about 30 seconds, so a single run collects a shortlist of them rather than a whole category. Match data from the page source is missing entirely on Valorant and League of Legends, where the ticker is the way to get results. Head-to-head records are derived from the matches in your own run, not from Liquipedia's complete history. A career-results lookup returns the placements the profile page itself lists; request the `/Results` subpage for the full history. Per-map detail beyond the score is not returned. Anything Liquipedia computes at render time and this Actor cannot resolve is reported as `null` with the field named in `unresolvedFields`, never approximated. Data on subpages, such as separate qualifier or group-stage pages, is only returned if you request those pages.

Liquipedia is a community-edited wiki. A record reflects what editors have published as of `lastEditedAt`, not verified fact, and coverage varies a lot between large and small game wikis. An `updated` record means the page text changed; it does not by itself prove a transfer happened.

Content is licensed under CC-BY-SA 3.0. If you republish it, attribute Liquipedia using the `pageUrl` on each record and keep derivative works under the same licence. Please keep your usage within Liquipedia's API Terms of Use, cache what you have already fetched rather than re-scraping it, and consider setting `contactEmail` for heavy or scheduled runs.

### Support

Found a page that parses wrong, or a field you need that is missing? Open an issue on the Issues tab of this Actor. Include the run ID, the exact input you used, and the Liquipedia page URL — that is usually enough to reproduce and fix it quickly.

# Actor input Schema

## `wiki` (type: `string`):

The Liquipedia game wiki to read, taken from the URL: `https://liquipedia.net/dota2/...` is `dota2`. Used for every bare page title and for category or search discovery. Entries in `pages` that are full URLs carry their own wiki and ignore this value. Examples: `dota2`, `counterstrike`, `leagueoflegends`, `valorant`, `rocketleague`, `overwatch`, `apexlegends`, `rainbowsix`, `starcraft2`, `mobilelegends`.

## `pages` (type: `array`):

The pages to scrape. Accepted formats include `ESL One/Birmingham/2024`, `https://liquipedia.net/dota2/ESL_One/Birmingham/2024`, `counterstrike:Natus Vincere`, and `valorant/TenZ`. Underscores, URL encoding, and `index.php?title=` links are all normalized, and Liquipedia redirects are followed, so `The International 2024` resolves to `The International/2024` automatically. Leave empty when you use `category` or `search` instead.

## `category` (type: `string`):

Optional. Enumerate every article in a Liquipedia category on the selected wiki and scrape all of it, for example `Tournaments`, `Teams`, `Players`, or `2024 Tournaments`. The `Category:` prefix is optional. Always combine this with a sensible `maxPagesPerWiki` value, because large categories hold thousands of pages.

## `search` (type: `string`):

Optional full-text search across articles on the selected wiki, for example `Champions 2024`. Matching pages are scraped in relevance order. Combine with `maxPagesPerWiki` to cap the run.

## `entityTypes` (type: `array`):

Optional filter applied after each page is parsed. `tournament` covers pages with a league infobox, `player` and `team` the corresponding profile infoboxes, `transfer` the rows of a transfer archive or feed, `match` the entries of the match ticker, `headtohead` the derived pairings, and `other` any page without an infobox. Leave empty to keep everything. Keep this stable across runs that share a monitor ID, otherwise filtered-out records look removed.

## `includeMatches` (type: `boolean`):

Add every `{{Match}}` on a tournament page as a `matches` array with opponents, date, best-of, and a derived score. Coverage depends on the wiki: Dota 2 and Overwatch pages carry per-map winners, Counter-Strike pages carry per-side round counts, and Valorant and League of Legends transclude their matches from LiquipediaDB so their pages carry none. Each match reports how its score was derived in `scoreSource`. Costs no extra requests.

## `includeBrackets` (type: `boolean`):

Add the playoff bracket as a `brackets` array: the layout, every `R1M1`-style slot, the round it belongs to, and the round's name read from the editors' own headings. Slots that are still empty are kept, so the shape of the bracket is reported even on the wikis that keep their results in LiquipediaDB. A bracket published only as `{{ShowBracket}}` is listed with `source: "liquipediadb"` and no slots, so "no bracket" stays distinguishable from "bracket not in the wikitext". Costs no extra requests.

## `includeStandings` (type: `boolean`):

Add the final placement table as a `standings` array - which team finished in which place, with prize money and points. Liquipedia builds this table from its own database when a page renders, so it needs a different API call that its terms limit to one request per 30 seconds. Enable it for a shortlist of tournaments, not for a large category crawl, and raise the run timeout to at least 30 seconds per tournament.

## `maxStandingsLookups` (type: `integer`):

Safety cap on the slow standings lookups described above. Each one takes about 30 seconds, so the default of 8 fits inside the 300-second default run timeout. Raise both together if you need more: allow roughly 30 seconds per tournament. Tournaments beyond the cap are still returned, with `standings` left out.

## `includeHeadToHead` (type: `boolean`):

Add one record per pair of opponents that met in the matches on the scraped pages, with series played, series won by each side, maps won, the tournaments involved, and the first and last meeting. Liquipedia's own head-to-head pages are database queries that the API does not expose, so these are derived from this run's matches and cover exactly the pages you scraped - `derivedFrom` and `tournaments` say so on every record. Costs no extra requests.

## `includeTransferFeed` (type: `boolean`):

Add the wiki's latest roster moves as one record per transfer: date, the players involved with their countries, the team left, the team joined, the roles on each side, and the sources cited. This feed is rendered from Liquipedia's database, so it costs one 30-second request per wiki. For bulk or historical transfers, put the quarterly archives such as `Transfers/2025/1st Quarter` into `pages` instead - those are read from the wikitext at full speed and cost nothing extra.

## `transferFeedPage` (type: `string`):

Optional. The page the transfer feed is read from. The default suits every wiki; override it only to read a different rendered transfer list.

## `includeMatchTicker` (type: `boolean`):

Add the wiki's match ticker as one record per match: the exact kickoff instant in UTC, both opponents, the tournament, the best-of, and the score once a match is finished. A match that has not started reports no score rather than the ticker's placeholder `0 : 0`. The ticker is rendered from Liquipedia's database, so it costs one 30-second request per wiki.

## `matchTickerPage` (type: `string`):

Optional. The page the match ticker is read from. The default suits every wiki; override it only to read a different rendered ticker.

## `includeResults` (type: `boolean`):

Add each player's or team's tournament history to their record: total career winnings, recent placements with prize money and opponents, and individual awards. Liquipedia computes all of it from its own database when a page renders, so it costs one 30-second request per profile. A profile page lists its most recent placements only - put the `/Results` subpage into `pages` for the full history.

## `maxResultLookups` (type: `integer`):

Safety cap on the slow career-result lookups described above. Each one takes about 30 seconds. Profiles beyond the cap are still returned, without the career fields.

## `maxPagesPerWiki` (type: `integer`):

Limit how many pages are fetched from each wiki. Set to `0` for every page the selection returns. Explicit `pages` entries are used first, then category and search results. Keep this stable across runs that share a monitor ID, because it decides which pages the change monitor compares. The prefilled health-check input returns one record.

## `includeWikitext` (type: `boolean`):

Add the page's full raw wikitext to each record as `wikitext`. Useful when you want to parse a section this Actor does not extract yet. Transfer archives are the exception: they expand into one record per move, so the page's wikitext is not attached to them. Leave disabled for smaller and cheaper datasets.

## `contactEmail` (type: `string`):

Optional. Liquipedia's API Terms of Use ask every client to identify itself with contact details. This Actor already sends its own Actor URL; adding your email makes the requests traceable to you, which is good practice for heavy or scheduled use.

## `monitorId` (type: `string`):

Persistent namespace for change tracking. Keep the same value for the same wiki, pages, and filters. Use another value for an independent monitor or schedule.

## `onlyChangesSince` (type: `string`):

Optional ISO 8601 timestamp such as `2026-08-01T00:00:00.000Z`. When set, the dataset contains only records detected as new, updated, or removed since that time. The first run creates the baseline and returns everything as `new`.

## `requestDelayMillis` (type: `integer`):

Optional extra pacing. Liquipedia's API Terms of Use allow at most one request per two seconds, and this Actor always honours that floor, so values below 2100 have no effect. Raise it only if you run several Liquipedia Actors from the same account at the same time.

## `useApifyProxy` (type: `boolean`):

Liquipedia's MediaWiki API works without a proxy, and this Actor is built to stay inside the published rate limits, so a proxy is normally unnecessary. Enable it only as a network fallback.

## `proxyConfiguration` (type: `object`):

Optional proxy configuration used only when `useApifyProxy` is enabled.

## `startUrls` (type: `array`):

Optional compatibility alias so input written for other Liquipedia Actors keeps working. Entries are normalized and combined with `pages`; new integrations should use `pages`.

## Actor input object example

```json
{
  "wiki": "dota2",
  "pages": [
    "ESL One/Birmingham/2024",
    "https://liquipedia.net/counterstrike/Natus_Vincere"
  ],
  "category": "Tournaments",
  "search": "Champions 2024",
  "entityTypes": [],
  "includeMatches": false,
  "includeBrackets": false,
  "includeStandings": false,
  "maxStandingsLookups": 8,
  "includeHeadToHead": false,
  "includeTransferFeed": false,
  "transferFeedPage": "Portal:Transfers",
  "includeMatchTicker": false,
  "matchTickerPage": "Liquipedia:Matches",
  "includeResults": false,
  "maxResultLookups": 4,
  "maxPagesPerWiki": 1,
  "includeWikitext": false,
  "contactEmail": "data@example.com",
  "monitorId": "default",
  "requestDelayMillis": 2100,
  "useApifyProxy": false
}
```

# Actor output Schema

## `records` (type: `string`):

No description

## `summary` (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 = {
    "wiki": "dota2",
    "pages": [
        "ESL One/Birmingham/2024"
    ],
    "maxPagesPerWiki": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("clintsa/liquipedia-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 = {
    "wiki": "dota2",
    "pages": ["ESL One/Birmingham/2024"],
    "maxPagesPerWiki": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("clintsa/liquipedia-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 '{
  "wiki": "dota2",
  "pages": [
    "ESL One/Birmingham/2024"
  ],
  "maxPagesPerWiki": 1
}' |
apify call clintsa/liquipedia-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,clintsa/liquipedia-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/15kktPvnXartDIQWS/builds/huHrqRVWSH7zMkX6a/openapi.json
