# pywikibot: the Python framework behind MediaWiki bots

> Pywikibot is the Python library Wikimedia projects use to talk to the MediaWiki API, and it ships a large set of general function scripts alongside the library. It gives you site and page objects, a bot class hierarchy with page generators, and Wikibase support, but it also imposes a real constraint: the code lives on Gerrit, this GitHub repository is only a mirror, and the scripts obey each wiki's own bot policy.

**wikimedia/pywikibot** — A Python library that interfaces with the MediaWiki API. This is a mirror from gerrit.wikimedia.org. Do not submit any patches here. See https://www.mediawiki.org/wiki/Developer_account for contributing.

- Repository: https://github.com/wikimedia/pywikibot
- Website: https://www.mediawiki.org/wiki/Manual:Pywikibot
- Stars: 778 · Forks: 223
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/wikimedia-pywikibot

## A framework, a library and a script collection

Pywikibot is described as a Python library that interfaces with the MediaWiki API version 1.31 or higher. Also included are various general function scripts that can be adapted for different tasks, and the documentation notes that the full code documentation covers the library excluding scripts.

That distinction between library and scripts is the first thing to understand. The library is the part you import and build on, and it is what you get from PyPI. The scripts are a large collection of ready-made utilities that came out of years of real bot work, and they are only in the git clone.

The audience is broad but specific: people automating edits on MediaWiki installations, Wikipedia, Wikimedia Commons and Wikidata, plus the maintainers of those projects. The topics list gives the range, with api-client, mediawiki, wikidata, wikimedia, commons and wikipedia among them. The implementation is Python, the licence is MIT, and the package metadata declares the development status as Production/Stable.

The repository itself sits in the Wikimedia organisation, but it is a mirror. The project description says so explicitly: this is a mirror from gerrit.wikimedia.org, do not submit any patches here, and see the Developer account page for contributing. The last push was on 2026-10-19, and there are no GitHub releases, because releases are cut and published from the Gerrit-hosted repository.

That setup is unusual and it is worth internalising before writing any code against it. The GitHub copy is for reading, browsing and installing. Changes go through Gerrit.

## Site and Page objects over the API

The basic usage example is five lines, and it shows the shape of the whole library.

```python
import pywikibot
site = pywikibot.Site('en', 'wikipedia')  # The site we want to run our bot on
page = pywikibot.Page(site, 'Wikipedia:Sandbox')
page.text = page.text.replace('foo', 'bar')
page.save('Replacing "foo" with "bar"')  # Saves the page
```

A Site object is a family and a language, here the English Wikipedia, and it is the object that holds the connection, the user credentials and the configuration. A Page is then a namespace and a title within that site, and it carries the text.

Two details in those lines carry most of the design. The text is read and written as a string on the page object, and saving requires an edit summary, which is the second argument to save. Edit summaries are not decoration on MediaWiki; they are how a human reviewer understands what a bot did, and most wiki bot policies require them.

The replace call is also a reminder that the library gives you wikitext as text and does not attempt to be a structured editor. There is a parser in the dependency list for that, and the library uses it for the operations that need it, but the everyday interface is string manipulation on wikitext.

So the abstraction is deliberately thin: objects for sites, pages and users, methods for reading and writing, and everything else left to your code.

## Bot classes, page generators and the script skeleton

For anything larger than a single page edit, the library provides bot classes, and the documented pattern is a class, a generator and a main function.

The example defines MyBot on top of ExistingPageBot, which sets the update_options dictionary with a text and a summary, and implements treat_page to load the current page, append text and call put_current. The bot is constructed with a generator and the options, and run.

The generator side is where the library earns its keep. pagegenerators.GeneratorFactory is constructed first, its handle_args is called with the local arguments to parse the generator options, and getCombinedGenerator returns the generator the bot will walk. A separate call, pywikibot.handle_args, parses the global options.

That split is the answer to the most common complaint about bot frameworks, which is that a script that is a one-off edit and a script that processes a million pages should not look the same. Here the walk over pages is a separate object from the logic applied to each page, and both are replaceable.

The main function is written to take *args and return None, and the pattern of iterating over the remaining arguments, partitioning each on a colon into an option and a value, and passing the recognised ones through, is what lets the script accept options like -summary and -text without a bespoke parser.

The scripts directory in the repository is the practical catalogue. It is the accumulated answer to questions like how do I find pages in a category, how do I search, how do I move files on Commons, and it is where you should look before writing a generator yourself.

## Wikibase and Wikidata through the same objects

Wikidata is not a separate client. It is reached through the same Site and Page objects, with a different repository layer on top.

The documented sample makes the chain clear:

```python
import pywikibot
site = pywikibot.Site('wikipedia:en')
repo = site.data_repository()  # the Wikibase repository for given site
page = repo.page_from_repository('Q91')  # create a local page for the given item
item = pywikibot.ItemPage(repo, 'Q91')  # a repository item
data = item.get()  # get all item data from repository for this item
```

The site is written as wikipedia:en, and data_repository resolves the Wikibase repository associated with it. From there, a Q-number is turned into a local page object by page_from_repository, and an ItemPage wraps the item so that get returns the item's data.

Two things are worth noting for anyone automating Wikidata. First, the item and the sitelinks are separate objects, which is why you need both a repository page and an item page for what looks like one entity. Second, the same library handles the Wikipedia side, so a bot that reconciles articles against items is a single program rather than two.

The repository also has a dedicated Wikibase-related dependency, python-stdnum, used by the cosmetic_changes script for validating identifiers such as ISBNs, which is a small example of how the extras reflect real bot work rather than imagined use cases.

## Installing: PyPI for the library, a clone for the scripts

There are two install routes and the README is explicit about which one gives you what.

From a clone, with the submodules:

```bash
git clone https://gerrit.wikimedia.org/r/pywikibot/core.git
cd core
git submodule update --init
pip install -r requirements.txt
python pwb.py <script_name>
```

From PyPI, the library without the scripts:

```bash
pip install pywikibot
pwb <scriptname>
```

The clone route is the one that matters for most bot work, because the scripts are the point of the collection, and pwb.py is the entry point that runs them. The submodule step is not optional: the repository uses git submodules and a clone without them will be missing parts of the tree.

The installation guide on mediawiki.org has more detail for advanced usage, and the two Dockerfile variants in the repository show a containerised approach, with one built on a python:3.12-slim base, installing the requirements, installing the package, setting PYTHONPATH to include the code and scripts directories, and using python /code/pwb.py as the entry point with version as the default command.

The requirements file is worth a look before you install, because it distinguishes mandatory dependencies from optional ones. The mandatory set is small: mwparserfromhell for MediaWiki markup parsing, packaging, and requests. The rest are pulled in by particular scripts or features, which is why the setup file defines them as extras such as eventstreams, isbn, Graphviz, Google, memento, wikitextparser, mysql, Tkinter, mwoauth and html.

That structure lets you install a minimal library for your own code and add the pieces a specific script needs, instead of dragging every optional dependency into every environment.

## Dependency pins that explain history

Two constraints in the requirements and setup files are worth reading closely, because they are the kind of detail that saves an afternoon of debugging.

The first is PyJWT. Both the requirements file and the setup extras pin it as PyJWT != 2.10.0, != 2.10.1, and the comment in both places references T380270, which is a Wikimedia task identifier. The requirement is commented as being due to that task, and the same pin appears in the mwoauth extra, so the OAuth path inherits it.

The second is GraalPy. The requirements file caps charset-normalizer at 3.4.5 and urllib3 below 2.7, but only for the GraalVM platform implementation, and the comment calls it a workaround for known GraalPy compatibility issues around SSL and charset detection. So a version constraint that looks arbitrary is in fact scoped to one runtime.

Pillow has a similar shape, marked not installable on GraalPy, Python 3.9 or PyPy below 3.11, and the markers in the requirements file encode exactly those conditions for CPython and PyPy.

Taken together, these say something about the project. It supports an unusually wide range of interpreters and runtimes, including GraalPy and PyPy, and it carries the version pins needed to keep those combinations working. That is maintenance work that never appears in a feature list.

The dependencies also cover the parts of the job that are easy to underestimate: requests for the API calls, mwparserfromhell and wikitextparser for parsing, mwoauth for OAuth, python-stdnum for validating identifiers, googlesearch-python and requests-sse for the search and event stream generators, pydot for the graph modules, and PyMySQL for the MySQL generator.

## Where the limits are, and how the project is governed

The boundaries are mostly about the platform rather than the code.

The first is the MediaWiki version. The library interfaces with the MediaWiki API version 1.31 or higher, so it does not run against older installations, and it is written against the modern API rather than the legacy interfaces.

The second is that Pywikibot is a client, not a bot host. It needs credentials, it needs the target wiki's bot policy to permit what you are doing, and it will happily make edits that a wiki's administrators may consider abuse. The library gives you the tools; the policy on each wiki decides what you are allowed to do with them. There is no global permission.

The third is the two extra requirements that bite newcomers. You need an account on the wiki, and for anything beyond reading you generally need bot rights, and OAuth configuration for the login flows. The Developer account page linked from the README is where that process starts.

The fourth is that a collection of scripts is not a product. The scripts are general function scripts adapted from real use, and the library is the part with a stable interface. The project publishes a ROADMAP.rst and points the release history at the changelog on doc.wikimedia.org rather than at release tags, and the setup.py file contains a documented release procedure that ends with a warning not to upload a development release to PyPI.

Governance follows the same pattern. The code is maintained on Wikimedia's Gerrit installation, contributions are reviewed there under a MediaWiki Code of Conduct, and the repository has SECURITY.md, CODE_OF_CONDUCT.md, an AUTHORS.rst and a HISTORY.rst alongside the code. The mirror you cloned to read the code is not the place where the project makes decisions.

## Conclusion

Adopt pywikibot if you are writing a bot or maintenance script against MediaWiki, Wikimedia Commons or Wikidata and want the authentication, page generators and Wikibase handling that already exist instead of rebuilding them against the raw API. Do not adopt it for a one-off API call that requests would handle, and do not plan to send patches to the GitHub repository, because it is a mirror and the review process runs on Gerrit. Before you start, read the installation guide for the deployment type you need, decide whether you want the library from PyPI or the full script collection from a clone, and check the bot policy of the specific wiki you intend to edit.

## FAQ

### How do I install Pywikibot with its scripts?

Clone https://gerrit.wikimedia.org/r/pywikibot/core.git, cd core, run git submodule update --init, then pip install -r requirements.txt, and run scripts with python pwb.py <script_name>. Installing from PyPI with pip install pywikibot gives you the library without the scripts.

### Can I submit a patch to the GitHub pywikibot repository?

No. The project description says this is a mirror from gerrit.wikimedia.org and that patches should not be submitted here. The code is maintained on Wikimedia's Gerrit installation, and the README points to the Developer account page for how to contribute.

### What is the difference between the Pywikibot library and its scripts?

The library is the part you import and it is what the PyPI distribution installs, while the scripts are a collection of general function scripts for tasks like categorisation, search and Commons work. The scripts come with a git clone, and the documentation notes that the code documentation covers the library excluding scripts.

### How do I read and write a Wikidata item with Pywikibot?

Create the site with pywikibot.Site('wikipedia:en'), call site.data_repository() to get the Wikibase repository, use repo.page_from_repository('Q91') for a local page and pywikibot.ItemPage(repo, 'Q91') for the item, then call item.get() to retrieve the item data.

### What are the mandatory dependencies and which Python versions are supported?

The mandatory dependencies are mwparserfromhell for MediaWiki markup parsing, packaging and requests, with everything else optional per script. The package metadata requires Python 3.9 or higher, the licence is MIT, and the last push was on 2026-10-19.

## Sources

- [Issues](https://github.com/wikimedia/pywikibot/issues)
- [License: MIT](https://github.com/wikimedia/pywikibot/blob/master/LICENSE)
- [Project website](https://www.mediawiki.org/wiki/Manual:Pywikibot)
- [README](https://github.com/wikimedia/pywikibot/blob/master/README.md)
- [wikimedia/pywikibot on GitHub](https://github.com/wikimedia/pywikibot)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/wikimedia-pywikibot
