Using MediaWiki's API for Automated Content Management

When the wiki talks back: a quick dive into MediaWiki’s API

Ever stared at a sprawling knowledge base and thought, “There’s got to be a smarter way to keep all this tidy?” You’re not alone. The MediaWiki Action API (the api.php endpoint you’ll see in every installation) is the quiet workhorse that lets scripts read, write, and wrangle pages without lifting a mouse.

Why treat MediaWiki as a content manager?

People often assume wikis are only for collaborative editing, but the official manual points out that, with the right plumbing, a MediaWiki install can behave like a lightweight CMS. You get revision history, granular permissions, and built‑in templating for free. The real kicker? All of that is reachable through HTTP calls.

  • Versioned content – every edit is a snapshot you can roll back to.
  • Fine‑grained ACLs – user groups, rights, and even per‑page protection.
  • Template engine – combine data with wikitext on the fly.

Combine those with automation, and you’ve got a system that can publish updates, sync external data, or even generate reports overnight.

The three pillars of the API

MediaWiki doesn’t throw a single monolith at you. It actually ships three related interfaces:

  1. Action API (api.php) – the bread‑and‑butter REST‑like endpoint for reading and writing pages.
  2. RESTBase – a newer, JSON‑first service that mirrors much of the Action API but with a cleaner URL schema.
  3. MobileFrontend API – geared toward apps, but still usable for generic automation.

For most automation tasks, the Action API is the go‑to. It’s well‑documented at the Wikimedia Developer Portal, and you can test it live with the api.php?action=help query string.

Getting your hands dirty: a minimal “fetch page” example

Suppose you need to pull the raw wikitext of a page called Project:Roadmap. A quick curl does the trick.

curl -G "https://example.org/w/api.php" \
  --data-urlencode "action=parse" \
  --data-urlencode "page=Project:Roadmap" \
  --data-urlencode "prop=wikitext" \
  --data-urlencode "format=json"

The response contains a parse.wikitext field with the exact source you’d see in the edit box. No HTML scraping needed.

Writing without breaking a sweat

Editing via the API feels a bit like sending a postcard: you need a token, you need to be logged in, and you better not forget the edit summary.

First, obtain a login token (one‑time use) and feed it back with your credentials.

curl -X POST -d "action=query&meta=tokens&type=login&format=json" \
  -c cookies.txt "https://example.org/w/api.php"

Then, use that token to log in.

curl -X POST -d "action=login&lgname=Bob&lgpassword=Secret123&lgtoken=YOUR_LOGIN_TOKEN&format=json" \
  -b cookies.txt -c cookies.txt "https://example.org/w/api.php"

Now you have an authenticated session (saved in cookies.txt) and can fire off an edit request.

curl -X POST -d "action=edit&title=Project:Roadmap§ion=new&text=== Q4 Goals ==\n* Finish prototype\n* Publish whitepaper&summary=Automated Q4 roadmap update&token=YOUR_CSRF_TOKEN&format=json" \
  -b cookies.txt "https://example.org/w/api.php"

Notice the section=new flag – it appends a fresh heading instead of overwriting the whole page. Handy when you’re only adding a slice.

Batch operations: the power of list and generator

If you’re thinking “I need to touch a hundred pages every night”, the API can do it without a loop in your code. The generator parameter pulls a list of pages and feeds them into a subsequent action.

Here’s a sketch in Python that updates the “last reviewed” timestamp on all pages in the “Policy” namespace.

import requests

API = "https://example.org/w/api.php"
session = requests.Session()

# Step 1: fetch a CSRF token
token_resp = session.get(API, params={
    "action": "query",
    "meta": "tokens",
    "type": "csrf",
    "format": "json"
})
csrf_token = token_resp.json()["query"]["tokens"]["csrftoken"]

# Step 2: generate pages we care about
gen_params = {
    "action": "query",
    "generator": "allpages",
    "gapnamespace": "4",   # namespace 4 = Project
    "gaplimit": "max",
    "prop": "info",
    "format": "json"
}
gen_resp = session.get(API, params=gen_params)
pages = gen_resp.json().get("query", {}).get("pages", {})

for page_id, page in pages.items():
    title = page["title"]
    # fetch current content
    content_resp = session.get(API, params={
        "action": "query",
        "prop": "revisions",
        "rvprop": "content",
        "titles": title,
        "format": "json"
    })
    rev = next(iter(content_resp.json()["query"]["pages"].values()))["revisions"][0]["*"]
    # append timestamp
    new_text = rev + f"\n{{{{Last reviewed|{datetime.date.today()}}}}}"
    # push edit
    edit_resp = session.post(API, data={
        "action": "edit",
        "title": title,
        "text": new_text,
        "summary": "Automated review timestamp",
        "token": csrf_token,
        "format": "json"
    })
    # you might want to check edit_resp for errors

That snippet is deliberately a tad noisy – real‑world scripts inevitably have a bit of trial‑and‑error code that never quite makes the final cut.

Handling rate limits and etiquette

Wikis are shared resources. The API will politely return a maxlag warning if the underlying database is under pressure. Respect it; back off for a few seconds and try again. A simple exponential back‑off loop (wait 1 s, then 2 s, then 4 s…) keeps you from being blocked.

Also, set a meaningful User-Agent header. The developers at Wikimedia love knowing who’s talking to their servers.

curl -A "AcmeBot/1.0 (+https://acme.example.org/bot)" "https://example.org/w/api.php?action=query&meta=siteinfo&format=json"

If you’re hammering a public wiki (like Wikipedia), you’ll want to register an OAuth consumer and use signed requests instead of the cookie‑based approach shown earlier. The OAuth flow is more involved, but the official guide walks you through every step.

Mixing external data with wikitext templates

One of the coolest tricks is to let the API feed a template that consumes JSON from an outside service. Imagine you maintain a product catalog in a MySQL database. A nightly job could push a JSON blob into a wiki page using the action=edit call, and a Lua module (via Scribunto) would parse that JSON and render a table on any page that includes the template.

Rough outline:

  1. Export the product list to products.json.
  2. Use the API to write that JSON into Template:ProductData.
  3. In a wiki page, call {{#invoke:ProductTable|render}} – the Lua code reads the JSON and spits out a nicely formatted table.

This approach gives you the best of both worlds: a relational database for the heavy lifting, and the wiki for the human‑friendly presentation.

Security tips you shouldn’t ignore

  • Never hard‑code passwords. Store them in environment variables or a vault and read them at runtime.
  • Validate input before sending. The API will accept anything, but malformed wikitext can break page layout.
  • Watch out for edit conflicts. If a page changes between your read and write steps, the API will reject the edit with a editconflict error. Re‑read the page and retry.
  • Limit the bot’s rights. Grant only the permissions it truly needs (usually edit and maybe move).

Real‑world use cases that show the API’s reach

Companies have built internal knowledge bases where each new ticket automatically creates a wiki page, tags it, and assigns it to the right team – all via the Action API. Academic labs use scripts to pull data from public Wikidata, enrich it, and push a summary back into their project wiki for collaborators to review. Even community sites employ bots that nightly archive talk‑page discussions, preserving the discourse for future reference.

What’s common across these stories? A small amount of code, a clear token strategy, and an appreciation for the API’s rate‑limit feedback.

Testing your integrations

The MediaWiki sandbox instance (https://sandbox.wikimedia.org/wiki/Main_Page) offers a safe playground. You can run action=edit calls there without affecting production data. Just remember to prefix page titles with a unique string (like User:Bob/TestPage) so you don’t accidentally overwrite someone else’s work.

If you prefer a local setup, Docker makes it painless:

docker run -d -p 8080:80 \
  -e MEDIAWIKI_DB_TYPE=sqlite \
  -e MEDIAWIKI_SITE_NAME="API Lab" \
  mediawiki

Spin that up, point your scripts at http://localhost:8080/w/api.php, and you have a sandbox that mirrors the production environment.

Wrapping it up (but not in a tidy bow)

MediaWiki’s API is a Swiss‑army knife for anyone who wants to treat a wiki as a dynamic content hub. You can fetch, edit, batch‑process, and integrate external data, all while keeping the niceties of version control and permissions. The learning curve isn’t steep, but a few human‑scale habits – like respecting rate limits and handling edit conflicts – go a long way toward a smooth operation.

If you ever find yourself juggling a spreadsheet of release notes and a wiki full of documentation, remember that a few lines of curl or a modest Python script can bridge that gap. The API is there, quietly waiting for you to ask the right question.

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe