# MediaWiki API Extractor (`datamule/mediawiki-api-extractor`) Actor

Point at ANY MediaWiki wiki's Action API (Wikipedia, Fandom, wiki.gg, or any corporate/OSS wiki) and pull structured data: full-text search, enumerate pages, list category members, fetch page text + revisions, or read site info. Handles continue-token pagination. Pay per record.

- **URL**: https://apify.com/datamule/mediawiki-api-extractor.md
- **Developed by:** [Datamule](https://apify.com/datamule) (community)
- **Categories:** Developer tools
- **Features:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 records

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

## MediaWiki API Extractor

**Point at ANY MediaWiki wiki's Action API and pull structured data — one actor, every wiki.**

Beyond Wikipedia, tens of thousands of sites run [MediaWiki](https://www.mediawiki.org):
every one of the 40,000+ [Fandom](https://www.fandom.com) communities, every
[wiki.gg](https://www.wiki.gg) gaming wiki, Wikimedia Commons / Wiktionary / Wikidata,
and countless corporate, government and open-source wikis. They all answer the identical
`api.php?action=query` grammar and paginate with the same `continue` token — so this single
actor covers every install, with no per-site scraper to maintain.

Give it the wiki's **API endpoint** (the URL ending in `/api.php` or `/w/api.php`) and pick a
**mode**. Each result row is a set of clean, flat columns plus a lossless `_raw` object holding
the untouched API item.

### Try it now

Paste this input to run a bounded search of English Wikipedia:

```json
{
  "apiUrl": "https://en.wikipedia.org/w/api.php",
  "mode": "search",
  "query": "cat",
  "namespace": 0,
  "maxRecords": 25
}
```

At the current FREE-tier rate, the Actor event price is **$0.0005 per record**
(**$0.50 per 1,000 records**). This example can emit at most 25 rows, so its maximum
capped Actor event charge is **$0.0125**. Apify infrastructure usage is additional.

### Modes

| Mode | What it does | Key input |
|------|--------------|-----------|
| **search** | Full-text search — one row per hit (page id, title, snippet, size, word count, timestamp) | `query` |
| **allpages** | Enumerate a namespace's pages alphabetically (page id, namespace, title) | `apFrom` (optional) |
| **category** | List a category's members, optionally filtered to pages / subcategories / files | `category` |
| **content** | For specific titles: plain-text extract, latest-revision metadata, page length, page URL, and optionally the raw wikitext | `titles` |
| **siteinfo** | One row of wiki metadata: name, MediaWiki version, language, base URL, article path, page & article counts | — |

Every list mode follows MediaWiki's pagination automatically up to your **Max records** cap.

### Example inputs

**Search Wikipedia:**

```json
{ "apiUrl": "https://en.wikipedia.org/w/api.php", "mode": "search", "query": "climate change", "maxRecords": 100 }
```

**List a Fandom category:**

```json
{ "apiUrl": "https://community.fandom.com/api.php", "mode": "category", "category": "Help", "maxRecords": 500 }
```

**Fetch page content from a wiki.gg wiki:**

```json
{ "apiUrl": "https://terraria.wiki.gg/api.php", "mode": "content", "titles": ["Terraria Wiki", "Boss"], "introOnly": true }
```

**Read a wiki's site info:**

```json
{ "apiUrl": "https://www.mediawiki.org/w/api.php", "mode": "siteinfo" }
```

### Finding a wiki's API endpoint

Most wikis link it from their `Special:Version` page. The endpoint is usually the site root plus
`/api.php` or `/w/api.php`:

- Wikipedia and Wikimedia sites: `https://en.wikipedia.org/w/api.php`, `https://commons.wikimedia.org/w/api.php`
- Fandom: `https://<wiki>.fandom.com/api.php`
- wiki.gg: `https://<wiki>.wiki.gg/api.php`

### Output

Records share one nullable schema, so datasets are clean and predictable across modes and wikis.
Common columns include `pageid`, `ns`, `title`, `snippet`, `size`, `wordcount`, `timestamp`,
`type`, `extract`, `wikitext`, `pageLength`, `revId`, `revTimestamp`, `revUser`, `revComment`,
`pageUrl`, and (in `siteinfo`) `sitename`, `generator`, `lang`, `base`, `pageCount`,
`articleCount`. Every row also carries `_raw` — the full original API item, so nothing is lost.

#### Genuine representative row

Selected fields from row 1 of a disposable verification run on **2026-07-14** using the exact
`Try it now` input above are shown below. These are point-in-time source values, not a promise of
current values; the omitted mode-specific columns were `null`, and `_raw` retained the original
API item.

```json
{
  "_apiUrl": "https://en.wikipedia.org/w/api.php",
  "_mode": "search",
  "_index": 0,
  "pageid": 6678,
  "ns": 0,
  "title": "Cat",
  "snippet": "The cat (Felis catus), also called domestic cat and house cat, is a small domesticated carnivorous mammal. It is a member of Felidae, the family of mammals",
  "size": 172618,
  "wordcount": 17486,
  "timestamp": "2026-07-09T18:54:54Z",
  "type": null,
  "extract": null,
  "sitename": null
}
```

### Export search results to CSV with Python (API recipe)

This script uses only the Python standard library. It starts one small Wikipedia search run,
waits for it to finish (up to 5 minutes), and writes `pageid`, `title`, `pageUrl` and `snippet`
to `mediawiki_search.csv`. Set your token first: `export APIFY_TOKEN=...` (Console → Settings →
API & Integrations). The token is sent as a Bearer header, never in the URL.

```python
import csv, json, os, sys, time, urllib.request

TOKEN = os.environ["APIFY_TOKEN"]
API = "https://api.apify.com/v2"
RUN_INPUT = {"apiUrl": "https://en.wikipedia.org/w/api.php", "mode": "search",
             "query": "cat", "namespace": 0, "maxRecords": 10}
MAX_CHARGE_USD = 0.01  # hard cap on this run's Actor event charges
OUT = "mediawiki_search.csv"

def call(method, path, body=None):
    req = urllib.request.Request(API + path, method=method,
        data=None if body is None else json.dumps(body).encode(),
        headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=60) as r:
        return json.load(r)

actor_id = call("GET", "/acts/datamule~mediawiki-api-extractor")["data"]["id"]
run = call("POST", f"/acts/{actor_id}/runs?maxTotalChargeUsd={MAX_CHARGE_USD}", RUN_INPUT)["data"]
deadline = time.time() + 300
while run["status"] in ("READY", "RUNNING") and time.time() < deadline:
    time.sleep(5)
    run = call("GET", f"/actor-runs/{run['id']}")["data"]
if run["status"] != "SUCCEEDED":
    sys.exit(f"Run {run['id']} ended {run['status']}; {OUT} was not changed.")

rows = call("GET", f"/datasets/{run['defaultDatasetId']}/items?clean=true&format=json")
fields = ["pageid", "title", "pageUrl", "snippet"]
with open(OUT + ".tmp", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
    w.writeheader()
    w.writerows(rows)
os.replace(OUT + ".tmp", OUT)  # only replace the old file after a successful run
print(f"Run {run['id']}: {len(rows)} rows -> {OUT}")
```

What the result means:

- **Succeeded with 0 rows** — the run worked but the wiki had no matches (or the charge cap
  could not cover one record). You get a CSV with only the header row. That is not an error.
- **Failed, aborted or timed out** — the script stops with the run ID and status and leaves
  any earlier `mediawiki_search.csv` untouched. Open the run's log in Console to see why. A
  failed run may still have written some rows; check its dataset before you rerun.
- **`maxRecords` vs `maxTotalChargeUsd`** — `maxRecords` caps how many rows you ask for;
  `maxTotalChargeUsd` caps what you can be charged. The run stops at whichever comes first.
  Ten rows cost at most $0.005 in `record` events at the FREE-tier price. Apify
  infrastructure usage is billed separately and is extra.
- **Source terms** — rows come from the public wiki API you point at. Wikipedia text is
  CC BY-SA; keep `pageUrl` next to any text you reuse so it can be attributed. Other wikis may
  have different terms, rate limits or login rules, so not every wiki will be reachable.

### Behavior & limits

- **No match / empty category / missing page** → returns cleanly (0 rows, or a row flagged
  `missing: true`), never crashes.
- **Not a MediaWiki endpoint** (an HTML page or a 404) → fails fast with a clear message.
- **Polite by default** — sends a descriptive User-Agent, respects the API's `maxlag` signal, and
  backs off on rate limits, so it plays nicely with community wikis.
- **Private wikis** — optionally supply a bearer token and/or extra request headers. These are
  never required for public wikis and are never logged.

### Source terms & responsible use

- MediaWiki provides the API protocol, but each wiki/provider controls its terms, content
  licence, attribution requirements, availability, access policy, and rate limits. Review the
  target wiki's terms, API policy, and content licence before collecting or reusing data, and
  preserve any required source, author, and licence attribution. This Actor does not grant rights
  to source content.
- Use modest caps, honour provider limits and `Retry-After` guidance, and do not bypass access
  controls. The Actor sends `maxlag=5`, throttles requests, and retries transient `maxlag`, 429,
  and server errors.
- Source counts, content, timestamps, and freshness can change. Results are point-in-time API
  responses; availability, completeness, speed, freshness, and total cost are not guaranteed.

### Troubleshooting

- **Invalid API URL** — use a complete `http://` or `https://` Action API URL ending in
  `/api.php` or `/w/api.php`, not an article, search-result, or `Special:` page URL.
- **HTML response or 404** — the URL usually points at a web page or a disabled/moved API. Find
  the Action API link on the wiki's `Special:Version` page and use that endpoint directly.
- **Missing mode-specific input** — `search` needs `query`, `category` needs `category`, and
  `content` needs one or more `titles`. `allpages` and `siteinfo` need no additional key input.
- **No match or missing page** — a search or category with no matches correctly emits 0 rows.
  Content mode can emit a row with `missing: true`; check spelling, namespace, and the target wiki.
- **`maxlag`, 429, or transient server error** — built-in backoff retries these responses. If the
  provider remains busy, reduce `maxRecords`, honour its retry window, and run again later.
- **Private or access-gated wiki** — provide the required bearer token or request headers and
  confirm that the account can call the Action API. A 401 or 403 means access is still denied.

### Pricing

Pay-per-event: **one `record` charge per row emitted.** At the current FREE-tier event price,
that is **$0.0005 per record** or **$0.50 per 1,000 records**. A run that returns nothing incurs
no `record` event charge. The 25-row `Try it now` example has a maximum capped Actor event charge
of **$0.0125**. Apify infrastructure usage is billed separately and is additional.

**Your max run charge is respected.** The Actor writes only the records your run's maximum charge covers, billing each one as it is written, and does not fetch another results page (or more content titles) than that budget can pay for. If the budget does not cover even one record, no request is sent to the wiki and nothing is billed. If a charge cannot be recorded, the run fails instead of silently continuing.

# Actor input Schema

## `apiUrl` (type: `string`):

The MediaWiki Action API endpoint of ANY wiki — it ends in /api.php or /w/api.php. Examples: https://en.wikipedia.org/w/api.php (Wikipedia), https://terraria.wiki.gg/api.php (wiki.gg), https://<yourwiki>.fandom.com/api.php (any of the 40,000+ Fandom communities), https://commons.wikimedia.org/w/api.php, https://www.mediawiki.org/w/api.php, or a corporate/government/OSS wiki. Tip: most wikis link their API at Special:Version; the endpoint is usually the site root + /api.php or /w/api.php.

## `mode` (type: `string`):

What to extract. search = full-text search (one row per hit). allpages = enumerate a namespace's pages alphabetically. category = list a category's members. content = fetch the text/extract + latest-revision metadata for specific titles. siteinfo = one row of wiki metadata (name, version, language, counts).

## `query` (type: `string`):

The full-text search query, used in search mode (the srsearch parameter). Supports the wiki's search syntax, e.g. climate change, or intitle:physics. Required in search mode; ignored otherwise.

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

The category whose members to list, used in category mode. Give the bare name (Physics) or the full title (Category:Physics) — the actor adds the Category: prefix if missing. On a non-English wiki use that wiki's localized namespace prefix (e.g. Kategorie:Physik). Required in category mode; ignored otherwise.

## `categoryType` (type: `string`):

Optionally restrict category members to a single type. Leave as 'all' for every member.

## `titles` (type: `array`):

The page titles to fetch in content mode — one per row (or, if you paste a single string, separated by newlines or | pipes). Example: Cat, Dog, Felidae. You can harvest titles from a search or category run first, then feed them here. Required in content mode; ignored otherwise.

## `introOnly` (type: `boolean`):

In content mode, return only the page's lead/intro section as the plain-text extract (faster, smaller) instead of the full article text. On by default; turn off to fetch the entire page extract.

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

In content mode, also fetch each page's raw wikitext (the latest revision's main-slot source) into the wikitext column. Off by default (adds payload). The plain-text extract is always included regardless.

## `namespace` (type: `integer`):

The numeric namespace to restrict to in search and allpages modes. 0 = main/article namespace (the default), 6 = File, 14 = Category, etc. Leave empty for main namespace.

## `apFrom` (type: `string`):

In allpages mode, begin enumeration at this page title (the apfrom parameter), e.g. Ba to start at pages beginning with 'Ba'. Leave empty to start from the beginning of the namespace.

## `apFilterRedir` (type: `string`):

In allpages mode, whether to include redirects. 'all' = both (default), 'nonredirects' = real pages only, 'redirects' = redirects only.

## `maxRecords` (type: `integer`):

Maximum total number of records to emit across pagination. The rendered first-run cap is 25; each emitted row is one billable `record` event. In content mode this also caps how many titles are fetched.

## `bearer` (type: `string`):

Optional bearer token for a private / access-gated wiki. Sent as Authorization: Bearer \*\*\* — NOT required for public wikis (Wikipedia, Fandom, wiki.gg) — leave empty. Never logged.

## `extraHeaders` (type: `object`):

Optional extra HTTP headers to send with every request, as a JSON object, e.g. {"x-api-key": "..."} for a wiki behind an API gateway. Leave empty for public wikis.

## Actor input object example

```json
{
  "apiUrl": "https://en.wikipedia.org/w/api.php",
  "mode": "search",
  "query": "cat",
  "categoryType": "all",
  "introOnly": true,
  "includeWikitext": false,
  "namespace": 0,
  "apFilterRedir": "all",
  "maxRecords": 25
}
```

# Actor output Schema

## `results` (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 = {
    "apiUrl": "https://en.wikipedia.org/w/api.php",
    "query": "cat",
    "namespace": 0,
    "maxRecords": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("datamule/mediawiki-api-extractor").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 = {
    "apiUrl": "https://en.wikipedia.org/w/api.php",
    "query": "cat",
    "namespace": 0,
    "maxRecords": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("datamule/mediawiki-api-extractor").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 '{
  "apiUrl": "https://en.wikipedia.org/w/api.php",
  "query": "cat",
  "namespace": 0,
  "maxRecords": 25
}' |
apify call datamule/mediawiki-api-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datamule/mediawiki-api-extractor"
        }
    }
}
```

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/cncdLZU6KCBmiyBR7/builds/Efoy5HA73H5d6cIyo/openapi.json
