How to Use MediaWiki’s API to Build a Custom Search Widget for Your Website

Introduction

MediaWiki powers Wikipedia and thousands of other wikis. Its Action API is a clean, REST‑like interface that lets external sites query a wiki for titles, snippets, images and more. In this guide we walk through the steps needed to turn that API into a lightweight, client‑side search widget that can be dropped into any web page.

Understanding the MediaWiki Action API

The API follows the pattern https://your‑wiki/w/api.php?action=…&format=json. The most relevant modules for a search widget are:

  • list=search – full‑text search of titles and content (API:Search).
  • generator=search – a generator that feeds the result set into other prop=… modules, allowing you to retrieve page properties in a single request (see the Stack Overflow answer that demonstrates the pattern).
  • prop=pageimages – thumbnail URLs for the matching pages.
  • prop=extracts – a short plain‑text excerpt (API:Extracts).

All requests must include format=json and, when calling from a browser on a different domain, the origin=* parameter to satisfy CORS.

Why Use a Generator?

Calling list=search first and then making a second request for each title quickly runs into rate limits. The generator technique merges the two steps:

https://en.wikipedia.org/w/api.php?
  action=query&
  generator=search&
  gsrsearch=YOUR_QUERY&
  gsrnamespace=0&
  prop=info|extracts|pageimages&
  exintro=1&explaintext=1&exsentences=2&
  piprop=thumbnail&pithumbsize=120&
  format=json&origin=*

This single URL returns a list of pages that match the query together with a short extract and a 120‑pixel thumbnail. The response format matches the query.pages object used throughout the API.

Building the Front‑End Widget

The widget consists of three parts:

  1. A text input where the user types a query.
  2. A fetch call that queries the API.
  3. DOM rendering of the results (title, thumbnail, snippet).

The following HTML provides a minimal skeleton:

<div id="mw-search-widget" class="mw-search-widget">
  <input type="search" id="mw-search-input" placeholder="Search Wikipedia…">
  <ul id="mw-search-results" class="mw-search-results"></ul>
</div>

Style the widget with a few lines of CSS (kept short for brevity):

.mw-search-widget { max-width: 400px; font-family: Arial, sans-serif; }
#mw-search-input { width: 100%; padding: 8px; box-sizing: border-box; }
.mw-search-results { list-style: none; margin: 0; padding: 0; }
.mw-search-results li { display: flex; padding: 6px 0; border-bottom: 1px solid #ddd; }
.mw-search-results img { margin-right: 8px; width: 48px; height: auto; }
.mw-search-results .title { font-weight: bold; }
.mw-search-results .snippet { color: #555; font-size: 0.9em; }

JavaScript logic

Below is a vanilla‑JS implementation that works on any modern browser. It throttles requests so that a new API call is made only after the user stops typing for 300 ms.

(() => {
  const endpoint = 'https://en.wikipedia.org/w/api.php';
  const input = document.getElementById('mw-search-input');
  const results = document.getElementById('mw-search-results');
  let timer = null;

  const buildUrl = query => {
    const params = new URLSearchParams({
      action: 'query',
      generator: 'search',
      gsrsearch: query,
      gsrnamespace: '0',
      prop: 'info|extracts|pageimages',
      exintro: '1',
      explaintext: '1',
      exsentences: '2',
      piprop: 'thumbnail',
      pithumbsize: '120',
      format: 'json',
      origin: '*',
    });
    return `${endpoint}?${params}`;
  };

  const render = data => {
    results.innerHTML = '';
    if (!data.query || !data.query.pages) {
      results.innerHTML = 'No results';
      return;
    }
    const pages = Object.values(data.query.pages);
    pages.sort((a, b) => a.index - b.index); // preserve API order
    for (const page of pages) {
      const li = document.createElement('li');
      const thumb = page.thumbnail ? `` : '';
      const title = `${page.title}`;
      const snippet = page.extract ? `${page.extract}` : '';
      li.innerHTML = `${thumb}${title}${snippet}`;
      li.addEventListener('click', () => {
        window.open(`https://en.wikipedia.org/wiki/${encodeURIComponent(page.title)}`, '_blank');
      });
      results.appendChild(li);
    }
  };

  const search = query => {
    if (!query) { results.innerHTML = ''; return; }
    fetch(buildUrl(query))
      .then(r => r.json())
      .then(render)
      .catch(err => console.error('API error', err));
  };

  input.addEventListener('input', e => {
    clearTimeout(timer);
    timer = setTimeout(() => search(e.target.value.trim()), 300);
  });
})();

The script does three things:

  • Builds the request URL with the generator pattern.
  • Calls fetch and parses the JSON response.
  • Creates a list item for each result, including the thumbnail, title and snippet.

Because the API response includes the index property, the results are displayed in the same order as the search engine returns.

Adding Pagination

MediaWiki returns a continue object when more results are available. To fetch the next page, keep the gsroffset value from the previous response and add it to the next request:

// Inside render()
if (data.continue && data.continue.gsroffset) {
  const nextBtn = document.createElement('button');
  nextBtn.textContent = 'More…';
  nextBtn.onclick = () => {
    fetch(buildUrl(query) + `&gsroffset=${data.continue.gsroffset}`)
      .then(r => r.json())
      .then(render);
  };
  results.appendChild(nextBtn);
}

This approach keeps the UI responsive while respecting the API's rate limits.

Security and Performance Tips

  • Rate limits – Wikimedia wikis impose a per‑IP request cap (default 200 req/min). Debounce input, use pagination, and avoid excessive parallel calls.
  • User-Agent – When you move the widget to a server‑side environment (Node, PHP, etc.) set a descriptive User-Agent header as recommended in API:Etiquette.
  • Cache results – Store recent queries in localStorage or an in‑memory map to avoid repeating identical requests.
  • HTTPS – Always call the API over HTTPS to protect query strings.

Testing with ApiSandbox

The MediaWiki site provides an ApiSandbox where you can paste the generated URL, tweak parameters, and see the raw JSON. It is invaluable for debugging property names, limit values and continuation tokens.

Deploying the Widget

Once you are satisfied with the local version, embed the HTML, CSS and JavaScript into any CMS, static site generator or plain HTML page. If you prefer a modular approach, wrap the code in an ES6 class or a small jQuery plugin. The widget works with any MediaWiki installation that exposes the default API endpoint; just replace https://en.wikipedia.org with your own wiki’s base URL.

Conclusion

MediaWiki's Action API is powerful enough to replace a full‑blown search engine for many use‑cases. By using the generator=search pattern you can fetch titles, extracts and thumbnails in a single request, keep network traffic low, and build a responsive search widget with only vanilla JavaScript. The code shown here is a solid starting point; you can extend it with facets, language selection, or even a custom ranking algorithm by swapping the srsearch parameter for a CirrusSearch profile.

Happy hacking, and may your searches always return the right page!

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe