wikimedia-api-strategy · git:20260818.a35dd44 · 2026-08-18 · sha256 b0082fca53bd76ed
wikimedia-api-strategy git:20260818.a35dd44A
Immutable. This exact content never changes and is served at /api/v1/blob/b0082fca53bd76ed.
---
name: wikimedia-api-strategy
description: Choose the right Wikimedia API or tool for the task — a decision framework covering REST API, Action API, SPARQL, SQL database replicas, EventStreams, and Pywikibot, with latency/complexity/authentication trade-offs for each approach
depends_on: [wikimedia-api-access]
license: MIT
compatibility: opencode
last_verified: 2026-08-18
skill_discovery_hints:
- keywords: ["which API", "best tool", "how to access", "API strategy", "choose between"]
- keywords: ["REST vs Action", "SPARQL vs API", "Pywikibot vs API", "SQL vs API"]
- keywords: ["performance", "fastest", "rate limit", "batch", "bulk", "scale"]
- keywords: ["read only", "read write", "analytics", "real time"]
---
> ⚠️ **User-Agent required:** All API calls below need a descriptive `User-Agent` header. See the **[wikimedia-api-access](../wikimedia-api-access/SKILL.md)** skill for the correct format, rate limiting, and error handling. If you get HTTP 403 or 429, load that skill before debugging.
>
> 💡 **Related skills:**
> - **[wikimedia-api-access](../wikimedia-api-access/SKILL.md)** — Base API patterns, User-Agent, rate limiting
> - **[wikidata](../wikidata/SKILL.md)** — SPARQL query patterns and Wikidata data model
> - **[pywikibot](../pywikibot/SKILL.md)** — Full bot framework for bulk editing
> - **[wikimedia-database](../wikimedia-database/SKILL.md)** — SQL database replicas via Toolforge SSH tunnel
> - **[wikimedia-eventstreams](../wikimedia-eventstreams/SKILL.md)** — Real-time event streaming
> - **[wikipedia-error-handling](../wikipedia-error-handling/SKILL.md)** — Error recovery for any approach
---
## Quick Decision Flowchart
```
What do you want to do?
│
├─ Read a single page or its summary?
│ → REST API (/page/summary, /page/html) [fastest, simplest]
│
├─ Read multiple pages with filters?
│ ├─ Simple filter (by category, template, user)?
│ │ → Action API (list=categorymembers, etc.) [good for <500 items]
│ ├─ Complex relational query across entities?
│ │ → SPARQL (Wikidata Query Service) [good for graph queries]
│ └─ Bulk analytics across millions of rows?
│ → SQL replicas (Toolforge database) [100-1000× faster than API]
│
├─ Monitor changes in real-time?
│ → EventStreams (SSE) [only way for live data]
│
├─ Edit pages, run bots, or bulk operations?
│ → Pywikibot [framework for all write ops]
│
└─ Check a page's patrol status or triage new pages?
→ PageTriage API [specialized extension]
```
---
## Reference: The Six Access Methods
| # | Method | Best For | Read/Write | Latency | Rate Limit | Auth Needed? | Base URL |
|:-:|--------|----------|:----------:|:-------:|:----------:|:------------:|----------|
| 1 | **REST API** | Single page reads, summaries, HTML content | Read only | Low (~200ms) | Per IP | No | `https://en.wikipedia.org/api/rest_v1/` |
| 2 | **Action API** | Multi-page queries, filtering, metadata, edits | Read + Write | Low (~200ms) | 200 req/s burst, 50 sustained | For edits | `https://en.wikipedia.org/w/api.php` |
| 3 | **SPARQL** | Complex graph queries, cross-entity relations | Read only | Medium (1-30s) | 1 req/s sustained, 60s timeout | No | `https://query.wikidata.org/sparql` |
| 4 | **SQL Replicas** | Bulk analytics, JOINs across tables, aggregates | Read only | Medium (~1s per query) | Connection pool limited | SSH tunnel + DB creds | Via Toolforge tunnel |
| 5 | **EventStreams** | Real-time monitoring, live feeds | Read only | Real-time (push) | None (server pushes) | No | `https://stream.wikimedia.org/v2/stream/` |
| 6 | **Pywikibot** | Bot operations, bulk edits, framework | Read + Write | Varies | Bot-specific | Bot password | Wraps Action API |
---
## Decision Trees by Task Category
### A. Reading Page Content
| If you need... | Use... | Because... |
|----------------|--------|------------|
| A page's first paragraph + thumbnail | **REST API** (`/page/summary/{title}`) | Fastest, simplest JSON, no pagination |
| Full rendered HTML of a page | **REST API** (`/page/html/{title}`) | Pre-rendered, ready for CSS/JS parsing |
| Raw wikitext of a page | **Action API** (`action=parse&prop=wikitext`) | REST API doesn't expose raw wikitext |
| Multiple pages' content at once | **Action API** with `titles=TITLE1\|TITLE2` | Batch up to 50 titles per call |
| A single revision's content | **REST API** (`/revision/{id}/content`) | Direct by revision ID, no page lookup needed |
**❌ Common mistake:** Using `action=parse` for a single page summary when the REST API's `/page/summary` returns cleaner JSON in one call.
**⚠️ Extracting lead text for LLM context?** Four approaches exist (REST
summary, TextExtracts, Parsoid HTML + DOM cleaning, LLM summarization) and none
is universally correct — they differ on fidelity, date/parenthetical loss, and
artifacts. `/page/summary` silently **strips parentheticals including dates**
(John Williams loses "(born February 8, 1932)"). See
[`references/lead-text-extraction.md`](references/lead-text-extraction.md) for
the full decision matrix, the Parsoid noise contract, and the
browser-render fidelity-test pattern.
### B. Querying Multiple Pages with Filters
| If you need... | Use... | Because... |
|----------------|--------|------------|
| Pages in a category (flat) | **Action API** (`list=categorymembers`) | Direct, paginated, namespace filterable |
| Pages in a category tree (recursive) | **Action API** with `cmtype=page\|subcat` loop, or **SQL replicas** (faster) | The API is simpler but slower; SQL is 100× faster for deep trees |
| Pages using a template | **Action API** (`list=embeddedin`) | Direct `eititle` parameter, paginated |
| Pages edited by a user | **Action API** (`list=usercontribs`) | Direct `ucuser` parameter, date-range filterable |
| Pages matching a text search | **Action API** (`list=search`) or **REST API** (`/search/page`) | REST API returns cleaner results; Action API supports more filters |
| Complex cross-page analytics (e.g., "pages in Category:Physics sorted by pageviews with quality assessments") | **SQL replicas** | Would require 500+ API calls. One SQL query with JOINs handles it in seconds. |
**❌ Common mistake:** Using `list=categorymembers` + individual API calls per page for analytics (e.g., fetching pageviews for 100 articles). Instead: use SQL replicas for the JOIN, or SPARQL + batch pageviews call.
### C. Graph Queries and Structured Data
| If you need... | Use... | Because... |
|----------------|--------|------------|
| "Find all X that have property Y" | **SPARQL** | One query replaces N API calls |
| Cross-language article mapping | **SPARQL** (`schema:about`/`schema:isPartOf`) | Sitelinks are not queryable via regular properties |
| Items missing a property (e.g., no image) | **SPARQL** (`FILTER NOT EXISTS`) | Can't express "does not have" in the Action API |
| Simple property lookup for a known item | **Action API** (`wbgetentities`) | Faster than SPARQL for single-item lookups |
| Batch entity classification (e.g., "which of these 100 items are people?") | **Action API** (`wbgetentities` batch of 50) | Faster than 100 SPARQL calls; use SPARQL only if you need subclass traversal |
**❌ Common mistake:** Using SPARQL for single-item lookups. `wbgetentities` returns the same data in ~200ms vs SPARQL's ~2-30s.
### D. Bulk Data and Analytics
| If you need... | Use... | Because... |
|----------------|--------|------------|
| Top 100 most-viewed pages in a category | **SQL replicas** (`page_props` JOIN `categorylinks`) | One query. Equivalent API approach would require: (1) fetch category members, (2) fetch pageviews for each = 100+ API calls |
| Correlation between quality and pageviews | **SQL replicas** (`page_assessments` JOIN `page_props`) | Single JOIN vs hundreds of API calls |
| Count of articles by quality class for a WikiProject | **SQL replicas** | `SELECT pa_class, COUNT(*) ... GROUP BY pa_class` in seconds |
| Quick sort by pageviews for a small set (<50 articles) | **REST API** (`/pageviews/per-article/...`) | Simpler than setting up a database tunnel |
| One-time ad-hoc analysis of a few articles | **Action API** | No SSH tunnel setup needed |
**❌ Common mistake:** Using the API for what should be a database query. If you find yourself writing a loop that makes >50 API calls for the same type of data, switch to SQL replicas or SPARQL.
### E. Real-Time Monitoring
| If you need... | Use... | Because... |
|----------------|--------|------------|
| Watch edits as they happen | **EventStreams** (`recentchange` stream) | Only way to get sub-second notifications |
| Monitor new page creations | **EventStreams** (filter `type == "new"`) | Faster than polling `list=recentchanges` |
| Build a live dashboard | **EventStreams** + periodic API snapshots | Stream gives live data, API fills history |
| Historical replay of past events | **EventStreams** (`?since=` parameter) | Up to 31 days of replay available |
| Check if a specific page was recently edited | **Action API** (`prop=revisions&rvlimit=1`) | Simpler than consuming a stream for one check |
**❌ Common mistake:** Polling `list=recentchanges` every few seconds instead of using EventStreams. Polling wastes API rate limits and introduces latency. The stream pushes data instantly.
### F. Editing and Bot Operations
| If you need... | Use... | Because... |
|----------------|--------|------------|
| Make a single edit | **Action API** (`action=edit`) | Simple, no framework setup needed |
| Make many edits with consistent logic | **Pywikibot** | Built-in rate limiting, edit summaries, conflict detection, page generators |
| Bulk template replacement across a category | **Pywikibot** (`replace.py` script) | Ready-made script, handles page loading/saving/conflicts |
| Scrape infobox data into Wikidata | **Pywikibot** (`harvest_template.py` script) | Purpose-built for this exact workflow |
| Move/delete pages in bulk | **Pywikibot** (built-in scripts) | Handles redirects, rate limits, and error recovery |
| Upload media to Commons | **Pywikibot** (`image_transfer.py`) | Preserves attribution history across wikis |
**❌ Common mistake:** Writing raw `action=edit` API calls for bulk operations. Pywikibot handles throttling (`put_throttle`), edit conflict detection, and automatic retry — all of which you'd need to implement from scratch with the raw API.
---
## Performance Comparison: API vs. SQL for the Same Task
This table shows how much work each approach requires for an equivalent result:
| Task | Action API | SPARQL | SQL Replicas | Speed Ratio |
|------|:----------:|:------:|:------------:|:-----------:|
| Top 50 articles in a category by pageviews | 50-200 calls (category + pageviews per article) | 1 query (with pageview data caveats) | **1 query** (JOIN) | **API:SQL ≈ 1:200** |
| Count articles by quality class for a WikiProject | 1 call per assessment × N pages | 1 query (if items have P31) | **1 query** (GROUP BY) | **API:SQL ≈ 1:500** |
| "Find all physicists born before 1900" | Not feasible (would need to scan all pages) | **1 query** | Complex (would need wikidata IDs) | **SPARQL:API ≈ 1:∞** |
| Fetch 10 random article summaries | **1 call** (REST API, 10 titles) | Overkill | Overkill | **REST is fastest** |
| Monitor new pages for 1 hour | 360 polls × API calls | Not applicable | Not applicable | **EventStreams:API ≈ 1:360** |
> 💡 **Rule of thumb:** If your task involves more than 50 individual items, consider whether a single SPARQL query or SQL query could replace the loop.
---
## SOP: Strategy Selection in Practice
### Step 1: Define Your Task
```
Is it:
A single page/entity? → Method 1 or 2
A batch of related items? → Method 2, 3, or 4
A complex relation? → Method 3 (SPARQL)
Bulk analytics? → Method 4 (SQL)
Real-time? → Method 5 (EventStreams)
Writing/editing? → Method 6 (Pywikibot) or 2 (Action API)
```
### Step 2: Check Constraints
```
Do you have:
Database access (SSH + credentials)? → SQL replicas available
A registered bot account? → Pywikibot available (higher rate limits)
No authentication at all? → REST/Action API (read-only), SPARQL, EventStreams
A need for speed? → REST API (fastest for reads), SQL (fastest for analytics)
```
### Step 3: Combine When Necessary
Some of the most powerful workflows **combine** methods:
```
1. Action API (categorymembers) → Get list of articles
2. + SPARQL (sitelinks) → Cross-reference with Wikidata
3. + REST API (pageviews) → Rank by popularity
4. + Pywikibot (editing) → Act on the results
```
Or an even faster alternative using SQL:
```
1. SQL replicas (JOIN) → All analytics in one query
2. + Pywikibot (editing) → Act on the results
```
### Step 4: Avoid Common Anti-Patterns
| ❌ Anti-Pattern | Why | ✅ Better Approach |
|---|---|---|
| Polling `list=recentchanges` every 5 seconds | Wastes rate limit, misses events | Use EventStreams for push-based monitoring |
| Fetching pageviews for 100+ articles one by one via REST API | 100+ API calls, takes minutes | Use SQL `page_props` JOIN or batch the API calls |
| SPARQL query for a single known QID | 2-30s response time for a 200ms task | Use `wbgetentities` via Action API |
| Raw `action=edit` for a 1000-page template migration | No throttling, no conflict handling, no edit summary enforcement | Use Pywikibot `replace.py` |
| Looping through category members with individual API calls per page | N+1 query problem | Use SQL replicas or batch API calls |
| Fetching full page wikitext just to get the page ID | Wasteful — ridiculously oversized response | Use `prop=info&inprop=url` (much smaller response) |
| Using the Action API when you need rendered HTML | Returns raw wikitext, requires client-side rendering | Use REST API `/page/html/{title}` for ready-to-render HTML |
| Implementing your own bot framework | You'll rewrite Pywikibot's throttling, conflict detection, and page generators from scratch | Use Pywikibot — it has 20+ years of battle-testing |
---
## Appendix: Quick Reference Cards
### Read Operations — Fastest Path by Data Size
```
1-10 items → REST API (single calls)
10-500 items → Action API (batch parameters, list modules)
500-5000 items → SPARQL (one query) or Action API (paginated)
5000+ items → SQL replicas (single JOIN/aggregation query)
```
### Write Operations — By Scale
```
1 edit → Action API (action=edit, simplest)
10-100 edits → Pywikibot (custom script with page generator)
100+ edits → Pywikibot (built-in scripts like replace.py, harvest_template.py)
```
### Real-Time Data
```
Live monitoring → EventStreams (SSE push)
Periodic checks → Action API (polling, but minimize frequency)
Historical replay → EventStreams (?since= parameter, up to 31 days)
```
### Authentication Requirements
```
Read-only, no auth → REST API, Action API (query), SPARQL, EventStreams
Read + Write → Bot password (Action API) or OAuth (Pywikibot)
SQL replicas → Toolforge SSH key + database credentials
Higher rate limits → OAuth (Lift Wing, REST API) or bot flag (Pywikibot)
```