Install Python Packages into the QGIS Environment
Sooner or later a PyQGIS script needs something QGIS does not ship — requests for an API, pandas for a summary table, openpyxl because somebody wants the output in a spreadsheet. The install itself is one command; getting it into the right Python is the part that goes wrong, because a machine running QGIS usually has three or four interpreters and pip install in a terminal reaches the wrong one.
This recipe belongs to Virtual Environments for GIS. It covers finding the interpreter QGIS is using, installing into it safely on each platform, keeping installs out of the way of QGIS upgrades, and the packages you must not touch.
Prerequisites
- QGIS 3.34 LTR (bundled Python 3.12) or newer.
- The Python console open, and network access for the package index.
- On Windows, the OSGeo4W shell if you prefer installing from a terminal.
Find out which Python QGIS is using
import sys
import site
print(sys.executable)
print(sys.version)
print(site.getusersitepackages())
print([p for p in sys.path if "site-packages" in p])
Breakdown: sys.executable in the QGIS console is the interpreter QGIS is running, and it is the only reliable answer — on Windows it is inside the OSGeo4W or standalone installation, on macOS inside the application bundle, on Linux usually /usr/bin/python3. getusersitepackages() is the per-user folder that is on the path without needing write access to the installation, which is where a --user install lands. Printing the site-packages entries on the path shows exactly where an import will be found from, which settles most "it says the module is not installed" arguments in one line.
Install from inside QGIS
import subprocess
import sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "--user", "requests"])
Breakdown: Calling pip as a module of a specific interpreter is the one form that cannot install into the wrong place. check_call raises on failure so a silent error is impossible, and the output appears in the console where you can read it. --user installs into the per-user site-packages folder, which needs no administrator rights and — importantly — survives a QGIS upgrade that replaces the installation folder. Restart QGIS afterwards: a package installed into a path Python has already scanned is not always importable until the interpreter restarts.
For repeatability, keep the list of extra packages in a file and install them together:
subprocess.check_call([sys.executable, "-m", "pip", "install", "--user",
"-r", "/home/ana/gis_requirements.txt"])
Breakdown: A requirements file makes the environment reproducible for a colleague and documents what your scripts actually need, which is otherwise discovered one ImportError at a time on somebody else's machine. Pin versions for anything you depend on heavily; leave them loose for utilities. This is also what a container image should install, as covered in Run PyQGIS in a Docker Container.
Platform notes that matter
Windows. Use the OSGeo4W shell — it sets the environment so python -m pip is the QGIS Python. Installing into the QGIS installation folder often requires administrator rights and is undone by the next upgrade, so prefer --user. Packages with compiled extensions must match the Python version and architecture; a wheel is fine, a source build usually needs tooling that is not present.
macOS. The Python inside the application bundle is code-signed, and writing into the bundle can break the signature. A --user install avoids it entirely.
Linux. QGIS from a distribution package uses the system Python, and installing there with pip can conflict with the package manager. Prefer the distribution's own package where one exists — python3-pandas rather than pip install pandas — and --user otherwise. Newer distributions refuse a system-wide pip install altogether unless you pass a flag, and that refusal is protecting you.
Do not upgrade what QGIS depends on
Some packages in the QGIS environment are not ordinary dependencies — they are compiled against the same libraries as QGIS itself, and replacing them breaks the application in ways that are hard to diagnose.
Never upgrade numpy, gdal, osgeo or PyQt with pip. GDAL's Python bindings must match the GDAL C++ library QGIS links against; a mismatched pair produces import errors, silently wrong raster reads, or a crash at startup. numpy is the base of the raster interfaces and is compiled against a specific ABI. PyQt must match the Qt QGIS was built with.
Be careful with anything that pulls them in as a dependency. A package requesting a newer numpy will happily upgrade it. Check first:
subprocess.check_call([sys.executable, "-m", "pip", "install", "--user",
"--dry-run", "some-package"])
Breakdown: --dry-run reports what would be installed or upgraded without doing it, which is the cheapest possible way to discover that a small utility wants to replace numpy. If it does, either pin the version you have with a constraint file, or find a lighter alternative. Recovering from a broken numpy in the QGIS environment usually means reinstalling QGIS, which is a bad afternoon.
Keep heavy work outside QGIS
Where a project needs a large scientific stack, installing all of it into the QGIS Python is rarely the right answer. Two better shapes exist.
Run the heavy analysis in its own virtual environment and have it read and write files — GeoPackages, GeoTIFFs, CSVs — that QGIS then reads. The interchange is a file, the environments stay independent, and neither can break the other.
Use QGIS as a library from your own environment instead, by putting the QGIS Python libraries on that environment's path. This is the standalone-script arrangement described in Running Python Scripts Outside QGIS Desktop, and it lets a data-science environment own the dependency set while still calling PyQGIS.
The rule of thumb: install small utilities into the QGIS Python, and keep large stacks in their own environment with files as the boundary. It is the arrangement that survives both a QGIS upgrade and a colleague's pip install.
QGIS version compatibility
| QGIS version | Python | Notes |
|---|---|---|
| 3.22 LTR | 3.9 | Packages must provide wheels for 3.9; user-site installs work identically. |
| 3.28 LTR | 3.9 | Identical. |
| 3.34 LTR | 3.12 | Baseline for this page. User site-packages folder is version-specific. |
| 3.40 / 3.44 | 3.12 | Identical mechanism. |
Because the user site folder is per Python version, an upgrade that changes the bundled Python — 3.9 to 3.12, for example — means reinstalling your packages. A requirements file makes that one command instead of an archaeology exercise.
Troubleshooting
ModuleNotFoundErrorafter a successful install. It went into a different interpreter. Reinstall withsys.executable -m pipfrom the console, and restart QGIS.- Permission denied. You are writing into the installation folder. Add
--user. - The install succeeds but QGIS crashes on start. A core dependency was upgraded. Reinstall QGIS and use
--dry-runnext time. - A compiled package will not build. No wheel for your platform and Python version. Look for a distribution package, or a pure-Python alternative.
- It works for you and not for a colleague. Nobody wrote the requirements down. Keep a requirements file next to the scripts.
- Packages disappear after an upgrade. They were in the installation folder, or the Python version changed. Reinstall from your requirements file.
Conclusion
Find the interpreter with sys.executable in the QGIS console, install with sys.executable -m pip install --user, and restart QGIS. Keep a requirements file so the environment is reproducible, never let pip upgrade numpy, gdal or PyQt, and check with --dry-run when a package's dependencies are unknown. For anything heavier than a utility, keep the stack in its own environment and exchange files.
Frequently Asked Questions
Can I use a virtual environment with QGIS? Not as QGIS's own interpreter. You can go the other way — add the QGIS libraries to your environment's path and use PyQGIS from it, which is the standalone-script arrangement.
Is there a plugin that installs packages? Several exist and they run the same pip command for you. Doing it explicitly is one line and leaves you knowing what happened.
How do I install a package for all users on a shared machine? Into the installation's site-packages with administrator rights, accepting that an upgrade will remove it. A managed image or a container is a better answer at that scale.
Why does QGIS ship its own numpy? Because the raster interfaces exchange arrays with it and must agree on the binary layout. That is precisely why replacing it breaks things.
Can I use conda? Yes, for a QGIS installed from conda-forge, where the whole stack is managed together. Mixing a conda environment with a system-installed QGIS is where the trouble starts.
Do plugins with dependencies handle this themselves?
Well-behaved ones declare requirements in metadata.txt and fail with a clear message rather than installing silently — see Write metadata.txt for a QGIS Plugin.