Run a Processing Algorithm from a Script in PyQGIS

Inside the QGIS Python Console, processing.run(...) just works because the application and the Processing framework are already initialized for you. In a standalone script — one launched from your terminal, a cron job, or a CI pipeline without the QGIS GUI — none of that setup exists yet. You have to start QgsApplication, initialize the Processing framework, and register the native algorithm provider before any processing.run call will succeed. This is the foundation of headless Batch Processing with PyQGIS.

This page covers the exact bootstrap sequence, how to discover an algorithm's parameter names with processing.algorithmHelp, how to run an algorithm, and how to read its output dictionary — then how to shut down cleanly.

Prerequisites

  • QGIS 3.34 LTR installed (bundled Python 3.12).
  • A terminal that can find the QGIS Python and libraries (use the OSGeo4W Shell on Windows, or python3 from the QGIS install on Linux/macOS).
  • A test input layer to run an algorithm against.

Run standalone scripts with the QGIS-aware Python, not your system Python, or the qgis.core import will fail. For environment setup details see Running Python Scripts Outside QGIS Desktop.

Initialize QgsApplication and Processing

The bootstrap has four parts: create a headless QgsApplication, call initQgis(), initialize the Processing plugin, and add QgsNativeAlgorithms to the registry so the native: algorithms become available.

import sys
from qgis.core import QgsApplication

# 1. Headless application (second arg False = no GUI)
qgs = QgsApplication([], False)

# 2. Point at the install prefix; "" lets QGIS auto-detect on most setups
QgsApplication.setPrefixPath("/usr", True)
qgs.initQgis()

# 3. Initialize the Processing framework
sys.path.append("/usr/share/qgis/python/plugins")
from processing.core.Processing import Processing
Processing.initialize()

# 4. Register the native (C++) algorithm provider
from qgis.analysis import QgsNativeAlgorithms
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())

Breakdown: QgsApplication([], False) starts QGIS without a GUI event loop. setPrefixPath tells QGIS where its resources live — /usr is typical on Debian/Ubuntu; on Windows it is your OSGeo4W apps/qgis (or qgis-ltr) folder. The plugins directory must be on sys.path before importing Processing. Processing.initialize() loads the GDAL/SAGA/GRASS providers, and addProvider(QgsNativeAlgorithms()) adds the native: set that Processing.initialize() does not register on its own — skip this step and native:buffer and friends will be reported as unknown.

Discover an Algorithm's Parameters

You should never guess parameter names. processing.algorithmHelp("provider:name") prints the full signature: every input, its type, acceptable values, and the output names.

import processing

processing.algorithmHelp("native:buffer")

Breakdown: This prints each parameter with its internal key (e.g. INPUT, DISTANCE, SEGMENTS, DISSOLVE, OUTPUT) and the expected value type. Those keys are exactly what you put in the parameters dictionary for processing.run. To list every available algorithm ID instead, iterate QgsApplication.processingRegistry().algorithms() and print each alg.id().

Run the Algorithm and Read OUTPUT

With parameters known, call processing.run(algorithm_id, params). It returns a dictionary; the geometry/file result is almost always under the OUTPUT key.

import processing

params = {
    "INPUT": "/data/points.gpkg",
    "DISTANCE": 250,            # in the layer's CRS units
    "SEGMENTS": 8,
    "DISSOLVE": False,
    "OUTPUT": "/data/output/points_buffered.gpkg",
}

result = processing.run("native:buffer", params)

print("Result keys:", list(result.keys()))
print("Output written to:", result["OUTPUT"])

Breakdown: processing.run executes synchronously and returns a dict keyed by the algorithm's output names. When OUTPUT was a file path, result["OUTPUT"] is that path string; when it was TEMPORARY_OUTPUT, it is an in-memory layer reference. Inspecting result.keys() reveals every output an algorithm produced — some return extra outputs such as a count or a report file alongside the main layer.

To use the result immediately, load it as a layer:

from qgis.core import QgsVectorLayer

out_layer = QgsVectorLayer(result["OUTPUT"], "buffered", "ogr")
print("Feature count:", out_layer.featureCount())

Breakdown: A file-path output is opened with the ogr provider. A TEMPORARY_OUTPUT result is already a layer object and needs no reloading — assign result["OUTPUT"] directly. This is the same dictionary pattern used when chaining processing algorithms, where one step's OUTPUT feeds the next step's INPUT.

A Complete Runnable Script

Putting the pieces together, including a clean shutdown with exitQgis():

import sys
from qgis.core import QgsApplication, QgsVectorLayer

qgs = QgsApplication([], False)
QgsApplication.setPrefixPath("/usr", True)
qgs.initQgis()

sys.path.append("/usr/share/qgis/python/plugins")
from processing.core.Processing import Processing
import processing
from qgis.analysis import QgsNativeAlgorithms

Processing.initialize()
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())

try:
    result = processing.run("native:buffer", {
        "INPUT": "/data/points.gpkg",
        "DISTANCE": 250,
        "SEGMENTS": 8,
        "DISSOLVE": False,
        "OUTPUT": "/data/output/points_buffered.gpkg",
    })
    layer = QgsVectorLayer(result["OUTPUT"], "buffered", "ogr")
    print(f"Done: {layer.featureCount()} features")
finally:
    qgs.exitQgis()

Breakdown: The try/finally guarantees exitQgis() runs even if the algorithm raises, releasing QGIS resources and flushing any open data sources. Omitting exitQgis() can leave file locks or incomplete writes in long-running batch jobs. This skeleton is the template every standalone Processing script should follow.

Run Algorithms in a Batch Loop

The whole point of scripting Processing is to repeat an algorithm across many inputs without touching the GUI. Once the bootstrap is in place, wrap processing.run in a loop over a folder of files, collecting outputs and logging failures so one bad file does not abort the run:

from pathlib import Path
import processing

source_dir = Path("/data/points")
output_dir = Path("/data/output")
output_dir.mkdir(parents=True, exist_ok=True)

results, errors = [], []
for src in sorted(source_dir.glob("*.gpkg")):
    out_path = output_dir / f"{src.stem}_buffered.gpkg"
    try:
        processing.run("native:buffer", {
            "INPUT": str(src),
            "DISTANCE": 250,
            "SEGMENTS": 8,
            "DISSOLVE": False,
            "OUTPUT": str(out_path),
        })
        results.append(out_path)
    except Exception as exc:
        errors.append((src.name, str(exc)))

print(f"Succeeded: {len(results)}  Failed: {len(errors)}")
for name, msg in errors:
    print(f"  {name}: {msg}")

Breakdown: Path.glob("*.gpkg") enumerates inputs; the try/except around each processing.run isolates per-file failures so the loop continues and reports them at the end. Building output names from src.stem keeps results traceable to their source. This is the standalone-script counterpart to the interactive Console patterns covered across Batch Processing with PyQGIS.

Capture Progress and Feedback

Long batch jobs benefit from a feedback object that surfaces progress and any algorithm warnings. Pass a QgsProcessingFeedback to processing.run to capture them programmatically:

import processing
from qgis.core import QgsProcessingFeedback


class LoggingFeedback(QgsProcessingFeedback):
    def pushInfo(self, info):
        print("INFO:", info)

    def reportError(self, error, fatalError=False):
        print("ERROR:", error)


result = processing.run(
    "native:buffer",
    {
        "INPUT": "/data/points.gpkg",
        "DISTANCE": 250,
        "SEGMENTS": 8,
        "DISSOLVE": False,
        "OUTPUT": "/data/output/points_buffered.gpkg",
    },
    feedback=LoggingFeedback(),
)

Breakdown: Subclassing QgsProcessingFeedback and overriding pushInfo/reportError routes the algorithm's internal messages to your own logger — invaluable when a headless job runs unattended and you need a record of what each step reported. The feedback keyword is accepted by every processing.run call.

QGIS Version Compatibility

The code targets QGIS 3.34 LTR (Python 3.12).

QGIS versionPythonNotes
3.28 LTR3.9Identical bootstrap; same Processing.initialize() and QgsNativeAlgorithms.
3.34 LTR3.12Baseline for this page.
3.40 / 3.443.12Same initialization API; algorithm IDs unchanged.

The standalone initialization sequence — QgsApplication, initQgis, Processing.initialize, addProvider(QgsNativeAlgorithms()) — has been stable across the entire 3.x line. Only the prefix path differs per platform and install.

Troubleshooting

  • Algorithm native:buffer not found. You skipped addProvider(QgsNativeAlgorithms()). Processing.initialize() alone does not register the native provider.
  • ModuleNotFoundError: No module named 'qgis'. You are running system Python. Launch with the QGIS/OSGeo4W Python instead.
  • No module named 'processing'. The plugins path is missing — append it to sys.path before importing Processing.
  • Application path not initialized. setPrefixPath points at the wrong directory or initQgis() was never called. Verify the prefix matches your install.
  • Output file already exists / is locked. A previous run left the GeoPackage open. Ensure exitQgis() ran, or write to a fresh path.

Conclusion

Running a Processing algorithm from a standalone script is straightforward once the bootstrap is correct: start a headless QgsApplication, initialize Processing, and explicitly register QgsNativeAlgorithms. From there, processing.algorithmHelp reveals exact parameter names, processing.run executes the algorithm, and the returned dictionary's OUTPUT key gives you the result to load or chain. Wrap it in try/finally with exitQgis() and you have a reliable, GUI-free pattern ready for batch jobs, schedulers, and CI.

Frequently Asked Questions

Why does processing.run fail outside the QGIS Console? The Console pre-initializes QGIS and Processing for you. A standalone script must do it manually — create QgsApplication, call initQgis, run Processing.initialize(), and add the native provider — before any algorithm call works.

How do I find an algorithm's parameter names? Call processing.algorithmHelp("provider:name"), for example processing.algorithmHelp("native:buffer"). It prints every parameter key and the expected value type, which map directly to your parameters dictionary.

What does result'OUTPUT' contain? It is the algorithm's primary result: a file-path string if you supplied a path, or an in-memory QgsVectorLayer/QgsRasterLayer if you used TEMPORARY_OUTPUT. Check result.keys() for any additional outputs.

Do I need to call exitQgis()? Yes, in standalone scripts. It releases resources and flushes data sources. Wrap the work in try/finally so it runs even when an algorithm raises an exception.