| name | wikimedia-diffs |
| description | Fetch, compare, and interpret diffs between Wikipedia page revisions — wikitext changes, visual differences, and diff statistics via the Action API and REST API |
| license | MIT |
| compatibility | opencode |
| depends_on | ["wikimedia-api-access"] |
| skill_discovery_hints | [{"keywords":["diff","compare revisions","edit comparison","action=compare","diffsize"]},{"keywords":["visual diff","rendered comparison","change detection","byte change"]}] |
| last_verified | "2026-06-10T00:00:00.000Z" |
⚠️ User-Agent required: The diff examples below use the Action API and REST API. All requests must include a descriptive User-Agent header or they will be blocked. See the wikimedia-api-access skill for the correct format.
SOP: Understanding Wikipedia Diffs
Wikipedia records every edit as a revision with a unique ID. A diff shows what changed between two revisions. There are two ways to get diffs:
| Approach | When to use | Endpoint |
|---|
| Action API | You need raw wikitext diff (markup changes) or diff metadata | action=compare |
| REST API | You need rendered HTML comparison (visual diff) | /compare |
| Browser URL | You want to view or share a diff in a web browser | index.php?diff=...&oldid=... |
Sharing a Diff Link
To share a link to a diff between two specific revisions:
https://en.wikipedia.org/w/index.php?title=PAGE_TITLE&diff=NEW_REV_ID&oldid=OLD_REV_ID
For example:
https://en.wikipedia.org/w/index.php?title=Talk:Geothermal_energy&diff=1352498037&oldid=1323528943
This works for any Wikimedia project — just change the hostname and provide the page title and two revision IDs. No API key or User-Agent is needed since this is a standard web URL.
SOP: Fetching Diffs via the Action API
The Action API's compare module returns an HTML table diff plus revision metadata. Common workflows that use diffs:
- Real-time patrol (see wikimedia-eventstreams): detect an edit → fetch its diff → classify the change
- Vandalism detection (see wikimedia-ml-services): score an edit for revert risk → fetch diff → analyze patterns
- Article monitoring: track changes to watched pages over time
Basic Diff Between Two Revisions (Absolute IDs Required)
import requests
headers = {"User-Agent": "MyBot/1.0 (user@example.com)"}
params = {
"action": "compare",
"fromrev": 123456789,
"torev": 123456790,
"prop": "diff|diffsize|ids|title|size",
"format": "json",
}
resp = requests.get("https://en.wikipedia.org/w/api.php", params=params, headers=headers, timeout=30)
data = resp.json()
Two-Step: Latest Edit to a Page
Since the API requires absolute revision IDs, first fetch the latest two:
import requests
s = requests.Session()
s.headers.update({"User-Agent": "MyBot/1.0 (user@example.com)"})
API = "https://en.wikipedia.org/w/api.php"
rv = s.get(API, params={
"action": "query", "prop": "revisions",
"titles": "Python (programming language)",
"rvlimit": "2", "rvprop": "ids", "format": "json",
}, timeout=30).json()
page = list(rv["query"]["pages"].values())[0]
revs = page["revisions"]
from_rev, to_rev = revs[1]["revid"], revs[0]["revid"]
diff = s.get(API, params={
"action": "compare",
"fromrev": from_rev, "torev": to_rev,
"prop": "diff|diffsize|ids|title|size", "format": "json",
}, timeout=30).json()
Response Fields
{
"compare": {
"fromtitle": "Python (programming language)",
"fromrevid": 123456789, # ← old revision ID (int)
"fromsize": 24500, # ← old page size in bytes (int)
"torevid": 123456790, # ← new revision ID (int)
"tosize": 24620, # ← new page size in bytes (int)
"diffsize": 150, # ← total churn: additions + removals (int, NOT net change!)
"*": "...HTML diff table..." # ← HTML table with class="diff" (string)
}
}
Access pattern:
cmp = data["compare"]
diffsize = cmp["diffsize"]
net_change = cmp["tosize"] - cmp["fromsize"]
if net_change >= 0:
added = net_change
removed = diffsize - net_change
else:
removed = -net_change
added = diffsize + net_change
| Field | Type | Description |
|---|
fromrevid / torevid | int | Revision IDs |
fromsize / tosize | int | Page sizes in bytes |
diffsize | int | Total bytes changed (added + removed churn — NOT net change) |
* | string | HTML table of the diff (parse with BeautifulSoup) |
SOP: Fetching Diffs via the REST API
The REST API provides a rendered HTML comparison that shows how the content looks different, not just the markup.
Endpoint
GET https://en.wikipedia.org/w/rest.php/v1/revision/{from}/compare/{to}
Example
import requests
headers = {"User-Agent": "MyBot/1.0 (user@example.com)"}
url = "https://en.wikipedia.org/w/rest.php/v1/revision/123456789/compare/123456790"
resp = requests.get(url, headers=headers, timeout=30)
data = resp.json()
Response Format
The response includes:
from — source revision ID and page info
to — target revision ID and page info
diff — array of change objects with type (add, remove, change, context), leftText (HTML), and rightText (HTML)
The HTML in leftText/rightText is rendered — templates are expanded, images shown, etc. This is useful for visual comparison.
Comparing by Slot
url = "https://en.wikipedia.org/w/rest.php/v1/revision/123456789/compare/123456790?slot=main"
SOP: Interpreting Diff Results
Detecting Edit Magnitude
cmp = data["compare"]
diffsize = cmp.get("diffsize", 0)
fromsize = cmp.get("fromsize", 0)
tosize = cmp.get("tosize", 0)
net = tosize - fromsize
if net >= 0:
approx_added = net
approx_removed = diffsize - net
else:
approx_removed = -net
approx_added = diffsize + net
if diffsize > 50000:
print("⚠️ Large-scale change — review needed")
if net < -20000:
print("⚠️ Significant net removal — possible blanking")
Parsing the HTML Diff
The diff HTML ("*" field) is a table with standard CSS classes. You can extract meaningful statistics from it:
from bs4 import BeautifulSoup
soup = BeautifulSoup(cmp["*"], "html.parser")
insertions = len(soup.find_all("td", class_="diff-addedline"))
deletions = len(soup.find_all("td", class_="diff-deletedline"))
changed_spans = len(soup.find_all("span", class_="diffchange"))
context_lines = len(soup.find_all("td", class_="diff-context"))
print(f"{insertions} insertions, {deletions} deletions")
print(f"{changed_spans} inline changes across {context_lines} context lines")
Extracting Changed Content
soup = BeautifulSoup(cmp["*"], "html.parser")
for td in soup.find_all("td", class_="diff-addedline"):
div = td.find("div")
if div:
print(f"+ {div.get_text()}")
for td in soup.find_all("td", class_="diff-deletedline"):
div = td.find("div")
if div:
print(f"- {div.get_text()}")
for span in soup.find_all("span", class_="diffchange"):
print(f"~ {span.get_text()}")
SOP: Classifying Diff Types (Change Pattern Analysis)
For real-time patrol, vandalism detection, and edit pattern analysis, classify diffs by their change characteristics. This is commonly combined with ML scores from wikimedia-ml-services.
Classification by Byte Statistics
cmp = data["compare"]
diffsize = cmp.get("diffsize", 0)
fromsize = cmp.get("fromsize", 0)
tosize = cmp.get("tosize", 0)
net = tosize - fromsize
if diffsize < 50:
change_type = "minor_tweak"
elif net < 0 and abs(net) > diffsize * 0.7:
change_type = "deletion_heavy"
elif diff size > 0 and net > diffsize * 0.7:
change_type = "addition_heavy"
elif diffsize > 1000 and abs(net) < diffsize * 0.3:
change_type = "replacement"
else:
change_type = "mixed"
Classification by Diff Table Structure
from bs4 import BeautifulSoup
soup = BeautifulSoup(data["compare"]["*"], "html.parser")
additions = len(soup.find_all("td", class_="diff-addedline"))
deletions = len(soup.find_all("td", class_="diff-deletedline"))
if additions == 0 and deletions > 0:
pattern = "pure_deletion"
elif deletions == 0 and additions > 0:
pattern = "pure_addition"
elif additions > 0 and deletions > 0:
ratio = additions / deletions
if ratio > 5:
pattern = "mostly_addition"
elif deletions / additions > 5:
pattern = "mostly_deletion"
else:
pattern = "balanced_edit"
Common Vandalism Signatures
| Diff Pattern | Suspicious? | Common Explanation |
|---|
| Large deletion (>50% of page), no edit summary | 🚨 High | Page blanking vandalism |
| Massive addition (>50KB), new user, no summary | 🚨 High | Possible copyvio or test edit |
| High churn with zero net change | ⚠️ Medium | Likely content replacement — could be legitimate or vandalism |
| Very small diff (<10 bytes) on main page | 🟢 Low | Likely typo fix or formatting |
| Single URL replacement in external links | ⚠️ Medium | Possible link spam |
| Diff adds invisible Unicode chars (zero-width, RTL markers) | 🚨 High | Obfuscated vandalism |
💡 Integration tip: Combine diff classification with ML revert-risk scores from revertrisk-language-agnostic (see wikimedia-ml-services) for a more accurate vandalism detector. High revert probability + addition-heavy diff = much more likely to be vandalism.
SOP: Anti-Patterns to Avoid
| ❌ Anti-Pattern | Why | ✅ Correct |
|---|
Using fromrev=prev or torev=cur (relative refs) | The API rejects non-integer revision IDs | Fetch absolute revision IDs first with action=query&prop=revisions |
Expecting a structured diff array | The API returns an HTML table in "*", not a structured array | Parse the HTML table with BeautifulSoup, or use the REST API for structured diffs |
Using diffsize as net bytes changed | diffsize is total churn (additions + removals), not net | Calculate net from fromsize / tosize |
| Parsing diff HTML with regex | The table structure is well-formed HTML | Use BeautifulSoup with CSS class selectors |
| Fetching diff for every revision in a page history | Rate limited | Fetch diffs selectively or with delays between requests |
Tooling
This skill includes helper scripts, reference docs, and templates:
🔧 Diff Fetcher (scripts/fetch-diff.sh)
Fetch a diff between two revisions or the latest change to a page.
./scripts/fetch-diff.sh --from-rev 123456789 --to-rev 123456790
./scripts/fetch-diff.sh --page "Python (programming language)"
./scripts/fetch-diff.sh --page "Albert Einstein" --stats
./scripts/fetch-diff.sh --page "Marie Curie" --json
🔧 Diff Stats (scripts/diff-stats.sh)
Analyze a diff and report statistics: lines/bytes added and removed, sections affected, templates changed, and potential issues (large deletions, blanking).
./scripts/diff-stats.sh --from-rev 123456789 --to-rev 123456790
./scripts/diff-stats.sh --page "Python (programming language)"
📚 Diff API Reference (references/diff-api.md)
Full reference for both the Action API compare module and the REST API /compare endpoint:
- Complete parameter list with
torelative and slot options
- All action types (add, remove, change, move, context)
- Diff table format documentation
- Comparison of Action API vs REST API approaches
- Error response guide (invalid revisions, protected pages)
- Rate limiting considerations
🐍 Revision Comparator (assets/compare-revisions.py)
Python script for fetching and analyzing diffs:
python3 assets/compare-revisions.py --from-rev 123456789 --to-rev 123456790
python3 assets/compare-revisions.py --page "Python (programming language)"
python3 assets/compare-revisions.py --page "Albert Einstein" --stats
python3 assets/compare-revisions.py --page "Marie Curie" --report
python3 assets/compare-revisions.py --page "Paris" --project fr.wikipedia
Cross-References