Wikipedia REST 403: which default User-Agent strings are blocked

Checked 2026-09-08 · Wikipedia and Wikidata as data

en.wikipedia.org returns 403 with a plain-text body to a request whose User-Agent is empty or is a well-known HTTP client default. requests, urllib3, Python-urllib, Go-http-client and axios defaults are all refused; curl, httpx, aiohttp and node-fetch defaults are not. The match is on the front of the string: 'myapp/1.0 python-requests/2.32.3' passes and the same two tokens the other way round does not. The same block covers /w/api.php and query.wikidata.org.

What we saw

An empty User-Agent gets 403 and a plain-text body, not JSON. Source (status and body, 2026-09-08).

403
Please set a user-agent and respect our robot policy https://w.wiki/4wJS. See also https://phabricator.wikimedia.org/T400119.

The default User-Agent of the requests library is refused; the default of curl is not. Source (status by User-Agent, 2026-09-08).

403	python-requests/2.32.3 ; 200	curl/8.14.1

The split is by client family, not by whether the string looks like a browser: httpx, aiohttp and node-fetch defaults pass, axios and Go defaults do not. Source (status by User-Agent, 2026-09-08).

403	<empty> / 200	curl/8.14.1 / 403	python-requests/2.32.3 / 403	python-urllib3/2.2.1 / 403	Python-urllib/3.13 / 200	httpx/0.27.0 / 200	aiohttp/3.9.5 / 200	Mozilla/5.0 / 200	myapp/1.0 / 403	Go-http-client/1.1 / 200	node-fetch/1.0 / 403	axios/1.7.2

The result is stable, not a rate-limit flap: three consecutive requests each way on a different article gave the same statuses. Source (three repeats per User-Agent, 2026-09-08).

python-requests/2.32.3 -> 403 403 403 ; curl/8.14.1 -> 200 200 200 ; axios/1.7.2 -> 403 403 403 ; Go-http-client/1.1 -> 403 403 403

The block is not specific to the REST summary route: /w/api.php and query.wikidata.org behave the same way. Source (status by User-Agent on the action API and on the Wikidata SPARQL endpoint, 2026-09-08).

python-requests/2.32.3 403 ; curl/8.14.1 200 (action API) — python-requests/2.32.3 403 ; curl/8.14.1 200 (query.wikidata.org/sparql)

The match is on the front of the string, not anywhere in it: putting your own token first makes the same compound string pass. Source (status by User-Agent, 2026-09-08).

200	myapp/1.0 python-requests/2.32.3 ; 403	python-requests/2.32.3 myapp/1.0 ; 200	MyBot/1.0 (contact@example.org) python-requests/2.32.3

A request that passes returns the summary JSON. Source (first fields of the response, 2026-09-08).

{"type":"standard","title":"Nikola Tesla","displaytitle":"<span lang=\"en\" dir=\"ltr\"><span class=\"mw-page-title-main\">Nikola Tesla</span></span>","namespace":{"id":0,"text":""},"wikibase_item":"Q9036"

The command

curl -sS -H 'User-Agent:' -o /tmp/w1.json -w '%{http_code}\n' 'https://en.wikipedia.org/api/rest_v1/page/summary/Nikola_Tesla'; cat /tmp/w1.json
403
Please set a user-agent and respect our robot policy https://w.wiki/4wJS. See also https://phabricator.wikimedia.org/T400119.

Checked 2026-09-08.

python3 -c "import requests; r=requests.get('https://en.wikipedia.org/api/rest_v1/page/summary/Nikola_Tesla', timeout=20); print(r.status_code, requests.utils.default_headers()['User-Agent']); print(r.text[:120])"
403 python-requests/2.32.3
Please set a user-agent and respect our robot policy https://w.wiki/4wJS. See also https://phabricator.wikimedia.org/T400119.

Checked 2026-09-08.

for ua in "" "curl/8.14.1" "python-requests/2.32.3" "python-urllib3/2.2.1" "Python-urllib/3.13" "httpx/0.27.0" "aiohttp/3.9.5" "Mozilla/5.0" "myapp/1.0" "Go-http-client/1.1" "node-fetch/1.0" "axios/1.7.2"; do c=$(curl -sS -o /dev/null -w '%{http_code}' -A "$ua" 'https://en.wikipedia.org/api/rest_v1/page/summary/Nikola_Tesla'); printf '%s\t%s\n' "$c" "${ua:-<empty>}"; done
403	<empty>
200	curl/8.14.1
403	python-requests/2.32.3
403	python-urllib3/2.2.1
403	Python-urllib/3.13
200	httpx/0.27.0
200	aiohttp/3.9.5
200	Mozilla/5.0
200	myapp/1.0
403	Go-http-client/1.1
200	node-fetch/1.0
403	axios/1.7.2

Checked 2026-09-08.

for ua in "python-requests/2.32.3" "curl/8.14.1" "axios/1.7.2" "Go-http-client/1.1"; do out=""; for i in 1 2 3; do out="$out $(curl -sS -o /dev/null -w '%{http_code}' -A "$ua" 'https://en.wikipedia.org/api/rest_v1/page/summary/Berlin')"; done; printf '%s ->%s\n' "$ua" "$out"; done
python-requests/2.32.3 -> 403 403 403
curl/8.14.1 -> 200 200 200
axios/1.7.2 -> 403 403 403
Go-http-client/1.1 -> 403 403 403

Checked 2026-09-08.

for ua in "myapp/1.0 python-requests/2.32.3" "python-requests/2.32.3 myapp/1.0" "requests" "MyBot/1.0 (contact@example.org) python-requests/2.32.3"; do c=$(curl -sS -o /dev/null -w '%{http_code}' -A "$ua" 'https://en.wikipedia.org/api/rest_v1/page/summary/Nikola_Tesla'); printf '%s\t%s\n' "$c" "$ua"; done
200	myapp/1.0 python-requests/2.32.3
403	python-requests/2.32.3 myapp/1.0
200	requests
200	MyBot/1.0 (contact@example.org) python-requests/2.32.3

Checked 2026-09-08.

for ua in "python-requests/2.32.3" "curl/8.14.1"; do printf '%s ' "$ua"; curl -sS -o /dev/null -w '%{http_code}\n' -A "$ua" 'https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&titles=Berlin&format=json'; done
python-requests/2.32.3 403
curl/8.14.1 200

Checked 2026-09-08.

for ua in "python-requests/2.32.3" "curl/8.14.1"; do printf '%s ' "$ua"; curl -sS -o /dev/null -w '%{http_code}\n' -A "$ua" -G 'https://query.wikidata.org/sparql' --data-urlencode 'query=SELECT ?x WHERE { wd:Q9036 rdfs:label ?x FILTER(lang(?x)="en") }' -H 'Accept: application/sparql-results+json'; done
python-requests/2.32.3 403
curl/8.14.1 200

Checked 2026-09-08.

User-Agentstatus
(empty)403
python-requests/2.32.3403
python-urllib3/2.2.1403
Python-urllib/3.13403
Go-http-client/1.1403
axios/1.7.2403
curl/8.14.1200
httpx/0.27.0200
aiohttp/3.9.5200
node-fetch/1.0200
Mozilla/5.0200
myapp/1.0200
myapp/1.0 python-requests/2.32.3200
python-requests/2.32.3 myapp/1.0403

Limits

Open question

Why axios and Go defaults are refused while node-fetch and httpx defaults are not. The pattern does not follow language or popularity in any way I could see.

Plain text