GuidesWIKIPEDIA API

Wikipedia API from JavaScript without CORS errors

The Action API at https://en.wikipedia.org/w/api.php only sends CORS headers when the URL contains origin=*; without it Wikipedia still answers 200, so every browser blocks the response. Add origin=* to the query string and leave credentials off. The REST summary, the rest.php search and Wikidata's SPARQL endpoint send Access-Control-Allow-Origin: * on their own. We tested 8 Wikimedia endpoints in Chromium, Firefox and WebKit on October 26, 2026: all work with no proxy and no key.

Add origin=* to Action API requests

Leave origin=* off an api.php request and Chrome's console shows No 'Access-Control-Allow-Origin' header is present on the requested resource while fetch() rejects with TypeError: Failed to fetch. Add origin=* and the response gets Access-Control-Allow-Origin: *, Access-Control-Allow-Credentials: false and MediaWiki-Login-Suppressed: true, and Wikipedia handles it as a logged-out request. MediaWiki's cross-site docs require origin as a URL query parameter, never a header or a POST body field; for a POST, the preflight has to carry it too.

Search-as-you-type with origin=*, a debounce and AbortController
// Runs in a module script on a page with <input id="search"> and <ul id="results">
const input = document.querySelector('#search');
const list = document.querySelector('#results');
let controller;
let timer;

input.addEventListener('input', () => {
  clearTimeout(timer);
  timer = setTimeout(() => suggest(input.value.trim()), 250);
});

async function suggest(text) {
  // A new keystroke cancels the request for the previous one
  controller?.abort();
  controller = new AbortController();
  if (!text) return list.replaceChildren();

  // origin=* is what makes the Action API send CORS headers
  const params = new URLSearchParams({
    action: 'query',
    generator: 'prefixsearch',
    gpssearch: text,
    gpslimit: '8',
    prop: 'pageimages|description',
    piprop: 'thumbnail',
    pithumbsize: '80',
    format: 'json',
    formatversion: '2',
    origin: '*',
  });

  try {
    const res = await fetch(`https://en.wikipedia.org/w/api.php?${params}`, {
      signal: controller.signal,
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}, Retry-After: ${res.headers.get('Retry-After')}`);
    const data = await res.json();
    // The Action API reports errors with HTTP 200, so check the body too
    if (data.error) throw new Error(`${data.error.code}: ${data.error.info}`);
    const pages = (data.query?.pages ?? []).sort((a, b) => a.index - b.index);
    list.replaceChildren(...pages.map(toItem));
  } catch (error) {
    if (error.name !== 'AbortError') console.error(error);
  }
}

function toItem(page) {
  const item = document.createElement('li');
  const link = document.createElement('a');
  link.href = 'https://en.wikipedia.org/wiki/' + encodeURIComponent(page.title.replaceAll(' ', '_'));
  link.textContent = page.title;
  item.append(link);

  if (page.description) item.append(' ', page.description);
  if (page.thumbnail) {
    const img = new Image(page.thumbnail.width, page.thumbnail.height);
    img.src = page.thumbnail.source;
    img.alt = '';
    item.prepend(img);
  }
  return item;
}

Here generator=prefixsearch with prop=pageimages|description returns suggestions with descriptions and thumbnails in one request, and the code sorts them by the index field, which holds the search rank. A 250 ms debounce and an AbortController drop stale requests. The Action API reports errors with HTTP 200, a MediaWiki-API-Error header and an error object in the body, so the code checks data.error as well as res.ok. Results are built with textContent, so a title can never inject markup.

Why you still get a CORS error

If a Wikipedia call still fails in the browser, it is usually one of these five. For anything else, see how to fix CORS errors.

  • No origin parameter on an api.php URL. Add origin=* to the query string.
  • credentials: 'include' together with origin=*. A wildcard origin cannot be used with credentials, and this fails in all 3 browsers. Leave credentials at the default.
  • origin set to your own domain, like origin=https://your-site.example. Only Wikimedia's own sites on the $wgCrossSiteAJAXdomains list are allowed; for any other domain Wikipedia returns 200 with a MediaWiki-CORS-Rejection: Origin mismatch header and no Access-Control-Allow-Origin, so the browser blocks it.
  • An origin value that differs from the browser's Origin header. Wikipedia answers 403 with 'origin' parameter does not match Origin header.
  • Fetching the article page at /wiki/Title (783,054 bytes of HTML for Ada_Lovelace) or index.php?action=raw for wikitext. Neither sends CORS headers; use /api/rest_v1/page/html/Title or /w/rest.php/v1/page/Title/html instead, which both send Access-Control-Allow-Origin: *.

Logged-in requests from your own site are not possible with origin=*; cookie-based CORS is only for Wikimedia's own domains and OAuth uses crossorigin= in the URL with an Authorization: Bearer token.

Article summaries from the REST API

The endpoint https://en.wikipedia.org/api/rest_v1/page/summary/TITLE always sends Access-Control-Allow-Origin: *, and there is no origin parameter. You get title, description, extract (the first paragraph as plain text), extract_html, thumbnail with source, width and height, originalimage, content_urls.desktop.page and wikibase_item, the Wikidata ID. Redirects resolve on the server, so summary/Einstein returns Albert Einstein. Check type before rendering a card, because normal articles return standard and disambiguation pages like Mercury return disambiguation. A missing page returns a 404 JSON body with CORS headers, so your code can read it; page/random/summary redirects to a random article; other languages work by changing the subdomain.

Article summary card from the REST API, no proxy
// Runs in a module script or the DevTools console
// The REST API sends Access-Control-Allow-Origin: *, so no proxy and no origin parameter
async function getSummary(title, lang = 'en') {
  const path = encodeURIComponent(title.trim().replaceAll(' ', '_'));
  const res = await fetch(`https://${lang}.wikipedia.org/api/rest_v1/page/summary/${path}`, {
    // Optional: names your app as Wikimedia asks, at the cost of a preflight
    headers: { 'Api-User-Agent': 'MyWikiCards/1.0 (https://example.com; you@example.com)' },
  });
  if (res.status === 404) return null;
  if (!res.ok) {
    throw new Error(`Wikipedia answered ${res.status}, Retry-After: ${res.headers.get('Retry-After')}`);
  }
  return res.json();
}

function renderCard(summary) {
  const card = document.createElement('article');
  const link = document.createElement('a');
  link.href = summary.content_urls.desktop.page;
  link.textContent = summary.title;

  const text = document.createElement('p');
  text.textContent = summary.extract;
  card.append(link, text);

  if (summary.thumbnail) {
    const img = document.createElement('img');
    img.src = summary.thumbnail.source;
    img.width = summary.thumbnail.width;
    img.height = summary.thumbnail.height;
    img.alt = '';
    card.prepend(img);
  }
  return card;
}

const summary = await getSummary('Ada Lovelace');
if (summary?.type === 'standard') document.body.append(renderCard(summary));
console.log(summary?.title, summary?.description, summary?.wikibase_item);

The optional Api-User-Agent header names your app the way Wikimedia's User-Agent policy asks. It is not a simple header, so the browser sends an OPTIONS preflight request first; the Action API and REST preflights carry no Access-Control-Max-Age and preflight caching is per URL, so leave the header out of search-as-you-type, where every new query is a new URL and would cost a second round trip.

Wikipedia endpoints tested for CORS

Every endpoint below was called with fetch() from http://localhost and http://127.0.0.1 pages in Chromium, Firefox and WebKit. How CORS works says what the endpoint needs before it sends Access-Control-Allow-Origin; Notes has the catch.

Wikipedia and Wikimedia endpoints tested with fetch() in Chromium, Firefox and WebKit, checked October 2026
EndpointWhat it returnsHow CORS worksNotes
en.wikipedia.org/w/api.php (Action API)Search, extracts, thumbnails, random pages, links and moreOnly with origin=* in the URLErrors come back as HTTP 200 with an error object
en.wikipedia.org/api/rest_v1/page/summary/TITLETitle, description, first paragraph, thumbnail, Wikidata IDAlways sends *404 JSON for missing pages, cached 5 minutes
en.wikipedia.org/api/rest_v1/page/random/summarySummary of a random articleAlways sends *303 redirect that fetch() follows
en.wikipedia.org/api/rest_v1/page/html/TITLEFull article HTMLAlways sends *651 KB for Ada Lovelace; the /wiki/ page sends no CORS headers
en.wikipedia.org/w/rest.php/v1/search/titleTitle suggestions with description and thumbnailAlways sends *search/page adds full-text excerpts
api.wikimedia.org/core/v1/wikipedia/en/search/pageSame results as the rest.php searchEchoes your originGradual deprecation from July 2026
query.wikidata.org/sparqlSPARQL results as JSONAlways sends *60 s query deadline, 5 parallel queries per IP
upload.wikimedia.org and thumb.wikimedia.orgImage filesAlways sends *fetch() can read the bytes

The api.wikimedia.org Core API still works, but Wikimedia shut down the API Portal in June 2026 and plans a gradual deprecation of the Core API starting July 2026. Use en.wikipedia.org/w/rest.php/v1/search/page for new code (its thumbnail URLs start with //, so prepend https:).

Query Wikidata with SPARQL from the browser

The endpoint https://query.wikidata.org/sparql sends Access-Control-Allow-Origin: *, answers with application/sparql-results+json and needs no key. The REST summary's wikibase_item field gives you the Wikidata ID (Q7259 for Ada Lovelace) to plug into a query, and GET responses are cacheable for 300 seconds.

Wikidata SPARQL query from the browser
// Runs in a module script or the DevTools console
// Wikidata's query service sends Access-Control-Allow-Origin: * and needs no key
const query = `
SELECT ?item ?itemLabel ?born ?died ?article WHERE {
  VALUES ?item { wd:Q7259 wd:Q937 wd:Q7186 wd:Q11641 }
  OPTIONAL { ?item wdt:P569 ?born. }
  OPTIONAL { ?item wdt:P570 ?died. }
  OPTIONAL { ?article schema:about ?item; schema:isPartOf <https://en.wikipedia.org/>. }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY ?born`;

const res = await fetch(
  'https://query.wikidata.org/sparql?format=json&query=' + encodeURIComponent(query),
);
if (!res.ok) throw new Error(`Query failed: HTTP ${res.status}`);

const { results } = await res.json();
console.table(
  results.bindings.map((row) => ({
    name: row.itemLabel.value,
    born: row.born?.value.slice(0, 10),
    died: row.died?.value.slice(0, 10),
    article: row.article?.value,
  })),
);

The Wikidata Query Service user manual sets a 60-second hard query deadline, 60 seconds of processing time per 60 seconds per client (User-Agent plus IP), 30 error queries per minute, and 5 parallel queries per IP, returning 429 with Retry-After when you cross them. In our tests a query for 4 people listed with VALUES took 0.9 to 1.7 seconds, while a query over all EU member states with capitals took 16 to 19 seconds, so keep queries narrow with VALUES or LIMIT. Our free movie API guide uses the same endpoint for film data.

Rate limits and User-Agent rules in 2026

Wikimedia started enforcing per-minute API rate limits on the Action API and REST APIs on March 12, 2026 and extended them to identified requests in the week of April 28, 2026:

  • Per-minute request limits: 10 for requests with nothing but an IP address, 200 for unauthenticated browser requests and for unauthenticated bots with a compliant User-Agent, 200 for logged-in new users, and 2,000 for established editors (bot-flagged accounts, stewards and Wikimedia Cloud Services are exempt).
  • When you hit 429: the response is 429 Too Many Requests with a Retry-After header (12 to 13 seconds in our test). When one server sent 49 requests through 24 parallel workers, 18 got a 429; re-sent one at a time 1.5 seconds apart, none did. The 429 response carries CORS headers and exposes Retry-After, so browser code can read it; if Retry-After is missing, wait at least 5 seconds or back off exponentially.
  • Concurrency and batching: Wikimedia's best practices ask for 3 or fewer concurrent requests, caching responses, and combining titles with | in a single Action API call.
  • Api-User-Agent in browsers: browser code typically cannot change User-Agent, which Wikimedia's policy accepts; it asks browser apps to add an Api-User-Agent header such as MyWikiCards/1.0 (https://example.com; you@example.com).
  • Server-side User-Agent: Node, Workers and Python code must send a descriptive User-Agent in the form client/version (contact) library/version, not a copied browser string. An empty one and python-requests/x both got 403 Please set a user-agent and respect our robot policy, and Wikidata's SPARQL endpoint also refuses requests with no User-Agent.
  • License: meta=siteinfo&siprop=rightsinfo lists Wikipedia text under Creative Commons Attribution-Share Alike 4.0 (CC BY-SA 4.0), so show the article title and link back when you display extracts.

No proxy is needed for any of these endpoints. Requests through cors.dev also returned 200 in our tests, but the proxy only adds a hop.

Good questions.

Why does the Wikipedia API give a CORS error?

The Action API (https://en.wikipedia.org/w/api.php) omits the Access-Control-Allow-Origin header unless you include origin=* in the URL query string. Add &origin=* to your request URL and leave credentials at the default.

Do I need a CORS proxy for the Wikipedia API?

No. All 8 Wikimedia endpoints we tested send CORS headers to the browser directly; only api.php needs origin=* in the URL.

Can I make logged-in Wikipedia API requests from my own site?

Not with origin=*, which runs every request as logged out. Cookie-based CORS only works from Wikimedia's own domains, so an app that acts as a user needs OAuth, with crossorigin= in the URL and an Authorization: Bearer header.

Does the Wikipedia API support JSONP?

Yes, api.php accepts callback=yourFunction with format=json, runs the request as logged out, and returns text/javascript. MediaWiki warns that a JSONP response runs as script in your page, and fetch() with origin=* worked in Chromium, Firefox and WebKit, so use CORS instead of JSONP.

What is the Wikipedia API rate limit?

Since March 12, 2026, Wikimedia enforces 200 requests per minute for unauthenticated browser requests and compliant bots, 10 per minute for requests with only an IP address, and 2,000 per minute for established editors. Going over the limit returns HTTP 429 with a Retry-After header and CORS headers.

How do I get a Wikipedia article summary in JavaScript?

Call fetch('https://en.wikipedia.org/api/rest_v1/page/summary/' + encodeURIComponent(title)). It sends Access-Control-Allow-Origin: * with no origin parameter and returns JSON with title, description, plain-text extract, thumbnail and wikibase_item.