Wikimedia REST API (en.wikipedia.org): spec vs live

Summary

Spec
https://en.wikipedia.org/api/rest_v1/?spec (OpenAPI 3)
Base URL
https://en.wikipedia.org/api/rest_v1
Run
, read-only GET requests, no credentials
Coverage
49 operations in the spec, 19 requests sent, 13 successful,19 operations observed
Hard violations
8 distinct schema locations
Reviewer note
Confirmed: media-list items carry undocumented leadImage and srcset; /page/lint returns an array where the spec says object; 400 errors come back as application/json, documented as application/problem+json.

Hard violations 8

A typed client generated from the spec would break on these.

Hide 8 findings
GET /page/lint/{title} > response 200 > application/json

the response body: expected "object" but got array

GET /page/lint/Earth → 200 application/json
expected ["object"] actual "array"
  • If the spec is the source of truth: Return "object" in the response body.
  • If the implementation is the source of truth: Change the type at paths["/page/lint/{title}"].get.responses["200"].content["application/json"].schema to "array".
GET /page/lint/{title}/{revision} > response 200 > application/json

the response body: expected "object" but got array

GET /page/lint/Earth/1 → 200 application/json
expected ["object"] actual "array"
  • If the spec is the source of truth: Return "object" in the response body.
  • If the implementation is the source of truth: Change the type at paths["/page/lint/{title}/{revision}"].get.responses["200"].content["application/json"].schema to "array".
GET /page/mobile-html-offline-resources/{title} > response 200 > application/json

the response body: expected "string" but got array

GET /page/mobile-html-offline-resources/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Mobile-HTML-Offline-Resources/1.2.1"
expected ["string"] actual "array"
  • If the spec is the source of truth: Return "string" in the response body.
  • If the implementation is the source of truth: Change the type at paths["/page/mobile-html-offline-resources/{title}"].get.responses["200"].content["application/json"].schema to "array".
GET /page/mobile-html-offline-resources/{title}/{revision} > response 200 > application/json

the response body: expected "string" but got array

GET /page/mobile-html-offline-resources/Earth/1 → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Mobile-HTML-Offline-Resources/1.2.1"
expected ["string"] actual "array"
  • If the spec is the source of truth: Return "string" in the response body.
  • If the implementation is the source of truth: Change the type at paths["/page/mobile-html-offline-resources/{title}/{revision}"].get.responses["200"].content["application/json"].schema to "array".
GET /page/title/{title} > response 200 > application/json > /count

response body at /count: required property "count" is missing

GET /page/title/Earth → 200 application/json
expected "count"
  • If the spec is the source of truth: Include "count" in the 200 response of GET /page/title/{title}.
  • If the implementation is the source of truth: Remove "count" from required at paths["/page/title/{title}"].get.responses["200"].content["application/json"].schema.
GET /page/title/{title} > response 200 > application/json > /items

response body at /items: expected "object" but got array

GET /page/title/Earth → 200 application/json
expected ["object"] actual "array"
  • If the spec is the source of truth: Return "object" in response body at /items.
  • If the implementation is the source of truth: Change the type at paths["/page/title/{title}"].get.responses["200"].content["application/json"].schema.properties["items"] to "array".
GET /page/title/{title}/{revision} > response 200 > application/json > /count

response body at /count: required property "count" is missing

GET /page/title/Earth/1 → 200 application/json
expected "count"
  • If the spec is the source of truth: Include "count" in the 200 response of GET /page/title/{title}/{revision}.
  • If the implementation is the source of truth: Remove "count" from required at paths["/page/title/{title}/{revision}"].get.responses["200"].content["application/json"].schema.
GET /page/title/{title}/{revision} > response 200 > application/json > /items

response body at /items: expected "object" but got array

GET /page/title/Earth/1 → 200 application/json
expected ["object"] actual "array"
  • If the spec is the source of truth: Return "object" in response body at /items.
  • If the implementation is the source of truth: Change the type at paths["/page/title/{title}/{revision}"].get.responses["200"].content["application/json"].schema.properties["items"] to "array".

Other body findings 12

Undocumented fields and loose constraints on successful responses. Documentation gaps, not violations.

Hide 12 findings
GET /page/media-list/{title} > response 200 > application/json > /items/0/leadImage

response body at /items/0/leadImage: property "leadImage" is not documented (and 34 more like it)

GET /page/media-list/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/MediaList/1.1.0"
actual "leadImage"
  • If the spec is the source of truth: Stop returning "leadImage" from GET /page/media-list/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "leadImage" to paths["/page/media-list/{title}"].get.responses["200"].content["application/json"].schema.properties["items"].items.properties.
GET /page/media-list/{title} > response 200 > application/json > /items/0/srcset

response body at /items/0/srcset: property "srcset" is not documented (and 30 more like it)

GET /page/media-list/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/MediaList/1.1.0"
actual "srcset"
  • If the spec is the source of truth: Stop returning "srcset" from GET /page/media-list/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "srcset" to paths["/page/media-list/{title}"].get.responses["200"].content["application/json"].schema.properties["items"].items.properties.
GET /page/media-list/{title} > response 200 > application/json > /items/28/gallery_id

response body at /items/28/gallery_id: property "gallery_id" is not documented (and 3 more like it)

GET /page/media-list/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/MediaList/1.1.0"
actual "gallery_id"
  • If the spec is the source of truth: Stop returning "gallery_id" from GET /page/media-list/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "gallery_id" to paths["/page/media-list/{title}"].get.responses["200"].content["application/json"].schema.properties["items"].items.properties.
GET /page/media-list/{title} > response 200 > application/json > /items/31/sources

response body at /items/31/sources: property "sources" is not documented

GET /page/media-list/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/MediaList/1.1.0"
actual "sources"
  • If the spec is the source of truth: Stop returning "sources" from GET /page/media-list/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "sources" to paths["/page/media-list/{title}"].get.responses["200"].content["application/json"].schema.properties["items"].items.properties.
GET /page/media-list/{title} > response 200 > application/json > /items/34/audio_type

response body at /items/34/audio_type: property "audio_type" is not documented

GET /page/media-list/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/MediaList/1.1.0"
actual "audio_type"
  • If the spec is the source of truth: Stop returning "audio_type" from GET /page/media-list/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "audio_type" to paths["/page/media-list/{title}"].get.responses["200"].content["application/json"].schema.properties["items"].items.properties.
GET /page/summary/{title} > response 200 > application/json > /content_urls

response body at /content_urls: property "content_urls" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "content_urls"
  • If the spec is the source of truth: Stop returning "content_urls" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "content_urls" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.
GET /page/summary/{title} > response 200 > application/json > /description_source

response body at /description_source: property "description_source" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "description_source"
  • If the spec is the source of truth: Stop returning "description_source" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "description_source" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.
GET /page/summary/{title} > response 200 > application/json > /namespace

response body at /namespace: property "namespace" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "namespace"
  • If the spec is the source of truth: Stop returning "namespace" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "namespace" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.
GET /page/summary/{title} > response 200 > application/json > /revision

response body at /revision: property "revision" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "revision"
  • If the spec is the source of truth: Stop returning "revision" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "revision" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.
GET /page/summary/{title} > response 200 > application/json > /tid

response body at /tid: property "tid" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "tid"
  • If the spec is the source of truth: Stop returning "tid" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "tid" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.
GET /page/summary/{title} > response 200 > application/json > /type

response body at /type: property "type" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "type"
  • If the spec is the source of truth: Stop returning "type" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "type" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.
GET /page/summary/{title} > response 200 > application/json > /wikibase_item

response body at /wikibase_item: property "wikibase_item" is not documented

GET /page/summary/Earth → 200 application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
actual "wikibase_item"
  • If the spec is the source of truth: Stop returning "wikibase_item" from GET /page/summary/{title} if it is not part of the contract.
  • If the implementation is the source of truth: Add "wikibase_item" to paths["/page/summary/{title}"].get.responses["200"].content["application/json"].schema.properties.

Undocumented error responses 9

3xx and 4xx statuses the spec does not mention, usually auth or not-found cases.

Show 9 findings
GET /data/lists/

request to "GET /data/lists/" conformed to the spec but was rejected with 400

GET /data/lists/?sort=updated → 400 application/json
actual 400
  • If the spec is the source of truth: Accept requests to GET /data/lists/ that conform to the spec; this one was rejected with 400.
  • If the implementation is the source of truth: Document the validation GET /data/lists/ performs (required fields, formats, constraints) so the spec reflects what is accepted.
GET /data/lists/ > response 400 > application/json

media type "application/json" is not documented for the 400 response

GET /data/lists/?sort=updated → 400 application/json
expected ["application/problem+json"] actual "application/json"
  • If the spec is the source of truth: Return one of application/problem+json from GET /data/lists/.
  • If the implementation is the source of truth: Add "application/json" under paths["/data/lists/"].get.responses["default"].content.
GET /data/lists/pages/{project}/{title}

request to "GET /data/lists/pages/{project}/{title}" conformed to the spec but was rejected with 400

GET /data/lists/pages/https%3A%2F%2Fen.wikipedia.org/Earth → 400 application/json
actual 400
  • If the spec is the source of truth: Accept requests to GET /data/lists/pages/{project}/{title} that conform to the spec; this one was rejected with 400.
  • If the implementation is the source of truth: Document the validation GET /data/lists/pages/{project}/{title} performs (required fields, formats, constraints) so the spec reflects what is accepted.
GET /data/lists/pages/{project}/{title} > response 400 > application/json

media type "application/json" is not documented for the 400 response

GET /data/lists/pages/https%3A%2F%2Fen.wikipedia.org/Earth → 400 application/json
expected ["application/problem+json"] actual "application/json"
  • If the spec is the source of truth: Return one of application/problem+json from GET /data/lists/pages/{project}/{title}.
  • If the implementation is the source of truth: Add "application/json" under paths["/data/lists/pages/{project}/{title}"].get.responses["default"].content.
GET /data/lists/{id}/entries/

request to "GET /data/lists/{id}/entries/" conformed to the spec but was rejected with 400

GET /data/lists/42/entries/?sort=updated → 400 application/json
actual 400
  • If the spec is the source of truth: Accept requests to GET /data/lists/{id}/entries/ that conform to the spec; this one was rejected with 400.
  • If the implementation is the source of truth: Document the validation GET /data/lists/{id}/entries/ performs (required fields, formats, constraints) so the spec reflects what is accepted.
GET /data/lists/{id}/entries/ > response 400 > application/json

media type "application/json" is not documented for the 400 response

GET /data/lists/42/entries/?sort=updated → 400 application/json
expected ["application/problem+json"] actual "application/json"
  • If the spec is the source of truth: Return one of application/problem+json from GET /data/lists/{id}/entries/.
  • If the implementation is the source of truth: Add "application/json" under paths["/data/lists/{id}/entries/"].get.responses["default"].content.
GET /page/ > response 404 > application/json

media type "application/json" is not documented for the 404 response

GET /page/ → 404 application/json
expected ["application/problem+json"] actual "application/json"
  • If the spec is the source of truth: Return one of application/problem+json from GET /page/.
  • If the implementation is the source of truth: Add "application/json" under paths["/page/"].get.responses["default"].content.
GET /page/talk/{title} > response 403 > text/html

media type "text/html" is not documented for the 403 response

GET /page/talk/Earth → 403 text/html; charset=utf-8
expected ["application/problem+json"] actual "text/html"
  • If the spec is the source of truth: Return one of application/problem+json from GET /page/talk/{title}.
  • If the implementation is the source of truth: Add "text/html" under paths["/page/talk/{title}"].get.responses["default"].content.
GET /page/talk/{title}/{revision} > response 403 > text/html

media type "text/html" is not documented for the 403 response

GET /page/talk/Earth/1 → 403 text/html; charset=utf-8
expected ["application/problem+json"] actual "text/html"
  • If the spec is the source of truth: Return one of application/problem+json from GET /page/talk/{title}/{revision}.
  • If the implementation is the source of truth: Add "text/html" under paths["/page/talk/{title}/{revision}"].get.responses["default"].content.

Reproduce

The first hard violation came from this request. Compare the response to the schema at the location named in the finding.

curl -s -H 'Accept: application/json' 'https://en.wikipedia.org/api/rest_v1/page/lint/Earth'

Data

If a finding is wrong, open an issue; if it is right, a pull request on the spec is welcome.