Read the PyQGIS API Docs and C++ Signatures

QGIS is written in C++, and Python reaches it through generated bindings. The PyQGIS documentation is generated from the same sources, so method signatures, types and notes are written with C++ in mind — const QgsFeatureRequest &request = QgsFeatureRequest(), bool *ok = nullptr, QgsVectorLayer::EditResult. Once you know how a handful of C++ conventions map to Python, the documentation becomes the fastest way to answer "what can this class do?" — faster than searching for examples that may be outdated.

This recipe belongs to QGIS API Architecture. It explains how to read a signature, how pointers, references and const translate, how out-parameters become returned tuples, how enums and flags are written in Python, how signals appear, and where Python-specific notes hide.

From a C++ signature to a Python callA C++ signature such as QgsFeatureIterator getFeatures(const QgsFeatureRequest &request = QgsFeatureRequest()) const maps to the Python call layer.getFeatures(request), with the default making the argument optional. A signature with an out-parameter, such as double measureLine(const QgsPointXY &p1, const QgsPointXY &p2) returning extra values through pointers, becomes a call that returns a tuple. Const and references disappear in Python.What survives the translationC++ signatureQgsFeatureIteratorgetFeatures(constQgsFeatureRequest &request= QgsFeatureRequest()) constPythonit = layer.getFeatures(req)it = layer.getFeatures()const, & and * vanishdefaults → optional args

Prerequisites

  • Access to the PyQGIS API reference for your version (qgis.org/pyqgis/) and, when needed, the C++ reference (api.qgis.org), which often has fuller descriptions.
  • A Python console to try calls as you read.

Read a method signature

Every method entry gives a return type, a name, typed parameters with optional defaults, and qualifiers. Most of it maps directly to Python.

from qgis.core import QgsProject, QgsFeatureRequest

layer = QgsProject.instance().mapLayersByName("districts")[0]

# C++: QgsFeatureIterator getFeatures(const QgsFeatureRequest &request = QgsFeatureRequest()) const
it_all = layer.getFeatures()                                    # default argument used
it_some = layer.getFeatures(QgsFeatureRequest().setLimit(5))    # explicit argument

# C++: QgsFeature getFeature(QgsFeatureId fid) const
f = layer.getFeature(3)
print(type(it_all).__name__, f.isValid())

help(layer.getFeatures)          # the docstring shows the Python signature

Breakdown: const, & and * describe how C++ passes and protects values; in Python they disappear — you pass the object. Parameters with = default become optional. Type names map to Python classes of the same name, and simple types map to Python types: QString is str, double is float, int and qint64 are int, bool is bool, QgsFeatureId is int. A trailing const on the method means it does not modify the object. help() on a bound method prints the docstring generated from the same documentation, which is often quicker than the website for a quick check.

Pointers, ownership and "transfers ownership"

Pointers in signatures — QgsVectorLayer *layer, QgsSymbol *symbol — mean the object is passed by address. The documentation's ownership notes then matter: they say who deletes the object later.

from qgis.core import QgsMarkerSymbol, QgsSingleSymbolRenderer

# C++: QgsSingleSymbolRenderer(QgsSymbol *symbol SIP_TRANSFER)
symbol = QgsMarkerSymbol.createSimple({"color": "#2563eb"})
renderer = QgsSingleSymbolRenderer(symbol)      # renderer now owns symbol

# C++: void setRenderer(QgsFeatureRenderer *r SIP_TRANSFER)
layer_pts = QgsProject.instance().mapLayersByName("stations")[0]
layer_pts.setRenderer(renderer)                 # layer now owns renderer

# do not reuse `symbol` for another renderer; clone it instead
other = QgsSingleSymbolRenderer(symbol.clone())

Breakdown: "Ownership of … is transferred" (shown as SIP_TRANSFER in sources) means the receiving object will delete it; after the call, the Python variable still refers to the object but you must not give it to anything else, or two owners will try to delete it — a classic crash. clone() makes an independent copy for reuse. Return values marked as "caller takes ownership" (SIP_FACTORY) are yours to keep. The full picture of ownership and its crashes is in object ownership and crashes.

Out-parameters become returned tuples

C++ methods often return extra values through pointer or reference parameters: bool *ok, QString &error. In Python those parameters disappear from the call, and the values come back as a tuple.

Out-parameters in PythonA C++ method with a return value and an out-parameter, such as bool exportLayer(..., QString *errorMessage), becomes a Python call that returns a tuple of the return value followed by each out-parameter in order. The Python docstring shows the tuple shape. Unpacking the tuple gives both values.Return value first, then the outsC++T method(…, X *out1, Y &out2)extra results via pointersPythonresult, out1, out2 = obj.method(…)see the docstring

from qgis.core import QgsExpression

# C++: static bool checkExpression(const QString &text, const QgsExpressionContext *context,
#                                  QString &errorMessage)
ok, message = QgsExpression.checkExpression('"name" ||', None)
print(ok, message)

from qgis.core import QgsGeometry, QgsPointXY

# C++: double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint,
#          int &nextVertexIndex, int *leftOf = nullptr, double epsilon = ...) const
line = QgsGeometry.fromWkt("LineString (0 0, 10 0)")
sqr_dist, nearest, next_vertex, left_of = line.closestSegmentWithContext(QgsPointXY(3, 2))
print(sqr_dist, nearest, next_vertex, left_of)

Breakdown: checkExpression returns its boolean result first, then the error message the C++ version writes into its reference parameter. closestSegmentWithContext shows the same rule with several outs: the squared distance it returns, then the nearest point, the index of the next vertex and the side of the line, all of which C++ writes into reference and pointer parameters. The pattern is consistent across the API: return value first, then out-parameters in order. When a method has no return value but has out-parameters, Python returns just the out-parameters. The Python docstring — from help() or the PyQGIS reference — shows the tuple shape explicitly, so read it rather than guessing from the C++ signature.

Enums and flags

Enums appear in the docs as QgsWkbTypes::Type, Qgis::GeometryType, QgsVectorLayer::EditResult. In Python, :: becomes ., and recent releases use scoped names.

Enum names across versionsAn enum documented as QgsWkbTypes::PolygonGeometry in older releases is written QgsWkbTypes.PolygonGeometry in Python on QGIS 3. After moving to the Qgis class it becomes Qgis.GeometryType.Polygon, the scoped form required by QGIS 4 and accepted by recent QGIS 3 releases. Writing the scoped form keeps code working on both.Write the scoped form, run on bothC++ docsQgsWkbTypes::PolygonGeometryold PythonQgsWkbTypes.PolygonGeometryscopedQgis.GeometryType.Polygon

from qgis.core import Qgis, QgsWkbTypes, QgsMapLayer

print(Qgis.GeometryType.Polygon)                      # scoped enum (QGIS 3.30+ and QGIS 4)
print(QgsWkbTypes.displayString(Qgis.WkbType.MultiPolygon))

# flags combine with |
categories = QgsMapLayer.StyleCategory.Symbology | QgsMapLayer.StyleCategory.Labeling
print(bool(categories & QgsMapLayer.StyleCategory.Labeling))

Breakdown: Many enums moved to the Qgis class during the 3.2x–3.3x series, and QGIS 4 requires the scoped form Class.EnumName.Value. The documentation of each enum notes when it moved and lists the old name; the old unscoped names still work on 3.x LTRs, so code using the new names runs on both. Flags are enums designed to combine with | and test with &. When an enum value is an integer in older code — TYPE: 0 in Processing parameters, for example — the docs list the values in order.

Signals and slots

Signals appear in their own section of a class's documentation. In Python, a signal is an attribute you connect a function to; its C++ parameters become the arguments your function receives.

# C++ signal: void featureAdded(QgsFeatureId fid)
def on_added(fid):
    print("feature added:", fid)

layer.featureAdded.connect(on_added)

# C++ signal: void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
layer.attributeValueChanged.connect(lambda fid, idx, value: print(fid, idx, value))

Breakdown: The signal's parameter list tells you what your handler receives, in order — here a feature id, then for attribute changes the field index and new value. Overloaded signals with several signatures are selected with square-bracket indexing in PyQt, which the docs show when it applies. Disconnect handlers when you no longer need them, especially in plugins, as explained in understanding QGIS signals and slots.

Find the Python-specific notes

Some methods behave differently in Python, and the documentation says so in notes that are easy to miss: "Not available in Python bindings", "In Python, this method returns…", "Since QGIS 3.x". Checking for them saves long debugging sessions.

import qgis.core as core

def api_info(cls_name, method):
    cls = getattr(core, cls_name, None)
    if cls is None:
        return f"{cls_name} not in qgis.core"
    m = getattr(cls, method, None)
    if m is None:
        return f"{cls_name}.{method} not available in Python (check version or bindings notes)"
    return (m.__doc__ or "").strip().splitlines()[0]

print(api_info("QgsVectorLayer", "getFeatures"))
print(api_info("QgsGeometry", "asWkb"))

Breakdown: Checking that a class and method exist in the running bindings catches the two common cases: a method added in a newer QGIS than the one installed, and a C++ method deliberately not exposed to Python. The first line of the docstring is the Python signature, often more useful than the C++ one. Notes marked "since QGIS 3.x" tell you the minimum version — important for plugins that declare a minimum QGIS version, as in the Python version compatibility guide. Exploring with dir() and help() is covered in exploring the PyQGIS API with dir and help.

QGIS version compatibility

The documentation is versioned: qgis.org/pyqgis/3.34/ and later each describe their release. Read the version you target, and use the master documentation only for upcoming features. The mapping rules here — const and references vanish, out-parameters become tuples, :: becomes . — are stable across QGIS 3 and QGIS 4; QGIS 4 additionally requires scoped enum names.

Troubleshooting

  • TypeError: arguments did not match any overloaded call. An argument has the wrong type; compare with the Python signature in help().
  • A method returns a tuple you did not expect. It has out-parameters; unpack them.
  • AttributeError for a documented method. The method is newer than your QGIS, or not exposed to Python; check the version note.
  • Crashes after passing objects around. Ownership was transferred; clone before reusing.

Conclusion

Read signatures by dropping const, & and *, treat defaults as optional arguments, watch ownership notes and clone transferred objects before reuse, unpack out-parameters from returned tuples, write enums with . and scoped names, connect signals with handlers matching their parameters, and check Python notes and version markers before relying on a method.

Frequently Asked Questions

Should I read the C++ or the Python docs? Start with the PyQGIS docs for Python signatures; consult the C++ docs for fuller descriptions and class diagrams.

What does SIP_SKIP mean in sources? The method is not available in Python.

Are Qt classes documented on the QGIS site? No — use the Qt documentation (PyQt or Qt for Python) for QColor, QDate and other Qt classes.

How do I find which class has a method I need? Search the docs, or dir() likely classes in the console; the class hierarchy pages show inherited methods.