API Stability

pycopg follows Semantic Versioning 2.0.0. At 1.0.0 the public API is frozen: no breaking changes will be made without a major version bump.

See API Stability on ReadTheDocs for the online version of this document.

SemVer Contract

pycopg uses MAJOR.MINOR.PATCH versioning:

  • MAJOR — incompatible API changes (breaking changes listed here)

  • MINOR — new functionality added in a backward-compatible manner

  • PATCH — backward-compatible bug fixes

Breaking vs Non-Breaking Changes

Change

Classification

Removing a public method or parameter

BREAKING (major bump)

Renaming a parameter without a deprecation alias

BREAKING (major bump)

Adding an optional parameter with a default value

Non-breaking (minor bump)

Changing a parameter default value

BREAKING (major bump)

Changing a return type

BREAKING (major bump)

Changing the exception type raised by a method

BREAKING (major bump)

Bug fix that changes documented behaviour

Patch (noted in CHANGELOG)

Deprecation Policy

Deprecated names are kept for the entirety of 1.x and removed only in 2.0. Every deprecated call emits DeprecationWarning on access or import. Update your code before 2.0 to silence these warnings.

Deprecated names introduced in 1.0.0:

  • Spatial where= parameter → use filter= instead (removed in 2.0)

  • Exception aliases ExtensionNotAvailable, TableNotFound, InvalidIdentifier, DatabaseExists → use *Error canonical names (removed in 2.0)

  • time_bucket / time_bucket_gapfill where= parameter → use filter= instead (removed in 2.0)

Note on deprecation warning timing (D-08): The deprecated exception name aliases (ExtensionNotAvailable, TableNotFound, InvalidIdentifier, DatabaseExists) emit DeprecationWarning at import/access time — when you write from pycopg.exceptions import ExtensionNotAvailable or pycopg.ExtensionNotAvailable. The warning does not fire at except OldName: catch time (this is intentional — the except clause receives the same class object and catching still works). This behaviour is per Python PEP 562 module __getattr__ semantics and is not a gap.

Stable API Contract

The table below documents the positional-vs-keyword-only boundary for every public method in the pycopg 1.0 API. This is the result of the FREEZE-03 consistency audit (Phase 41, Plan 04).

Legend:

  • Positional — can be passed without a keyword name (required args, or the documented “primitive I/O” carve-out below)

  • Keyword-only — must be passed as param=value; enforced by * separator in the function signature

D2/D5 carve-out: The raw-SQL execution primitives — execute, fetch_one, fetch_val, fetch_all, stream (both Database and AsyncDatabase) and maint.explain — intentionally keep params (and autocommit on execute) as positional-with-default. The (sql, params) two-positional convention is established and expected by users of the primitive I/O layer.

Database / AsyncDatabase — Flat Core

Method

Positional args

Keyword-only params

execute

sql, params, autocommit

(none — D2/D5 carve-out)

execute_many

sql, params_seq

(none — both required)

fetch_one

sql, params

(none — D2/D5 carve-out)

fetch_val

sql, params

(none — D2/D5 carve-out)

fetch_all

sql, params

(none — D2/D5 carve-out)

stream

sql, params, batch_size

(none — D2/D5 carve-out)

insert_many

table, rows

schema, on_conflict

upsert_many

table, rows, conflict_columns

update_columns, schema

upsert

table, row, conflict_columns

update_columns, schema

delete_where

table, where

schema

update_where

table, values, where

schema

exists

table, where

schema

count

table

where, schema

notify

channel

payload

insert_batch

table, rows

schema, on_conflict, batch_size

copy_insert

table, rows

schema, columns

paginate

table, limit

offset, order_by, where, descending, schema

from_dataframe

df, table

schema, if_exists, primary_key, index, dtype

to_dataframe

(none)

table, schema, sql, params

from_geodataframe

gdf, table

schema, if_exists, primary_key, spatial_index, geometry_column, srid

to_geodataframe

(none)

table, schema, sql, geometry_column, params

db.spatial.* / async_db.spatial.*

Method

Positional args

Keyword-only params

contains

table

geom, schema, filter, into, srid

intersects

table

geom, schema, filter, into, srid

dwithin

table

geom, distance, schema, filter, into, srid

distance

table

geom, schema, filter, into, srid

nearest

table

geom, schema, filter, into, limit, srid

area

table

geom, schema, filter, into

perimeter

table

geom, schema, filter, into

centroid

table

geom, schema, filter, into

buffer

table

geom, distance, schema, filter, into

transform

table

geom, srid, schema, filter, into

within

left_table, left_geom, right_table, right_geom

schema, filter, into

create_spatial_index

table

column, schema, name

list_geometry_columns

(none)

schema

union

table

geom, schema, other_geom, filter, into, columns, order_by, limit

difference

table

geom, schema, other_geom, filter, into, columns, order_by, limit

intersection

table

geom, schema, other_geom, filter, into, columns, order_by, limit

simplify

table

geom, schema, tolerance, preserve_topology, preserve_collapsed, filter, into, columns, order_by, limit

convex_hull

table

geom, schema, filter, into, columns, order_by, limit

make_valid

table

geom, schema, method, keep_collapsed, filter, into, columns, order_by, limit

collect_geometries

table

geom, schema, group_by, filter, into, order_by, limit

union_aggregate

table

geom, schema, group_by, filter, into, order_by, limit

extent

table

geom, schema, group_by, srid, filter, into, order_by, limit

as_geojson

table

geom, schema, max_decimal_digits, include_bbox, include_crs, into, columns, filter, order_by, limit

as_text

table

geom, schema, max_decimal_digits, into, columns, filter, order_by, limit

as_mvt

table

geom, schema, tile_z, tile_x, tile_y, layer_name, extent, columns, feature_id_column, srid, filter

Note on extent: into="rows" (default) returns a PostgreSQL box2d text string (e.g. "BOX(0 -1,0 1)"). into="gdf" returns a real geometry tagged with srid (default 4326) so GeoPandas can load it — the active geometry column is "extent" (D-04/D-05). No columns= parameter on the aggregate methods — the SELECT is the group columns plus the single aggregate expression (D-02).

Note on within: 4 required positional identifiers (left_table, left_geom, right_table, right_geom) stay positional. schema= is keyword-only (FREEZE-03 D1 clean break).

Note on as_geojson precision (D-06): max_decimal_digits=6 is the default — GeoJSON is typically used as a web payload where 6 decimal degrees is sub-metre precision and keeps response sizes small. as_text defaults to max_decimal_digits=None (full precision, no silent truncation) because WKT is used for exact interchange. This asymmetry is intentional. The include_bbox=False / include_crs=False options map to the ST_AsGeoJSON bitmask (bit1=bbox, bit2=crs); both default to off.

as_text(max_decimal_digits=...) version note: passing max_decimal_digits= uses the two-arg ST_AsText(geom, ndigits) form, which requires PostGIS >= 3.1. The default (None, full precision, one-arg form) works on all PostGIS 3.0+ servers.

Note on as_mvt empty-tile contract and SRID rescue (D-04/D-05): as_mvt always returns bytes — never None or a memoryview. An empty tile (no features in the requested tile envelope) returns b"" (empty bytes). If the stored geometry has an unknown or wrong SRID, pass srid=<int> to inject ST_SetSRID before the ST_Transform to EPSG:3857. ST_AsMVT, ST_AsMVTGeom, and ST_TileEnvelope require PostGIS ≥ 3.0. Since the pycopg PostGIS floor is already 3.0, this is documented for completeness and not gated at runtime.

db.schema.* / async_db.schema.*

Method

Positional args

Keyword-only params

create_database

name

owner, template

drop_database

name

if_exists

database_exists

name

(none)

list_databases

(none)

(none)

create_extension

name

schema, if_not_exists

drop_extension

name

if_exists, cascade

list_extensions

(none)

(none)

has_extension

name

(none)

create_schema

name

if_not_exists, owner

drop_schema

name

if_exists, cascade

list_schemas

(none)

(none)

schema_exists

name

(none)

list_tables

(none)

schema

table_exists

name

schema

list_columns

table

schema

columns_with_types

table

schema

drop_table

name

schema, if_exists, cascade

truncate_table

name

schema, cascade

table_info

name

schema

row_count

name

schema

add_primary_key

table, columns

schema, name

add_foreign_key

table, columns, ref_table, ref_columns

schema, ref_schema, name, on_delete, on_update

add_unique_constraint

table, columns

schema, name

create_index

table, columns

schema, name, unique, method, if_not_exists

drop_index

name

schema, if_exists

list_indexes

table

schema

list_constraints

table

schema

primary_key

table

schema

foreign_keys

table

schema

sequences

(none)

schema

views

(none)

schema

describe

table

schema

db.timescale.* / async_db.timescale.*

Method

Positional args

Keyword-only params

create_hypertable

table, time_column

schema, chunk_time_interval, if_not_exists, migrate_data

enable_compression

table

segment_by, order_by, schema

add_compression_policy

table

compress_after, schema

add_retention_policy

table, drop_after

schema

list_hypertables

(none)

(none)

hypertable_info

table

schema

show_chunks

table

older_than, newer_than, schema

drop_chunks

table

older_than, newer_than, schema, dry_run

add_dimension

table, column

partition_type, number_partitions, chunk_interval, schema, if_not_exists

add_reorder_policy

table, index_name

schema, if_not_exists

create_continuous_aggregate

view_name, select_sql

schema, materialized_only, with_no_data

refresh_continuous_aggregate

view_name

window_start, window_end, schema

add_continuous_aggregate_policy

view_name, start_offset, end_offset

schedule_interval, schema, if_not_exists

time_bucket

table, time_column, bucket_width, aggregates

filter, schema, into

time_bucket_gapfill

table, time_column, bucket_width, start, finish, aggregates

filter, schema, into

Note on time_bucket / time_bucket_gapfill filter=: The filter= parameter accepts a raw SQL fragment (structural SQL authored by the caller), not a dict-equality predicate. This was renamed from where= in 1.0 (D3 decision, FREEZE-03). The old where= emits DeprecationWarning through 1.x and is removed in 2.0.

db.admin.* / async_db.admin.*

Method

Positional args

Keyword-only params

create_role

name

password, login, superuser, createdb, createrole, inherit, replication, connection_limit, valid_until, in_roles, if_not_exists

drop_role

name

if_exists

role_exists

name

(none)

list_roles

(none)

include_system

alter_role

name

password, login, superuser, createdb, createrole, connection_limit, valid_until, rename_to

grant_role

role, member

with_admin

revoke_role

role, member

(none)

grant

privileges, on, to

object_type, schema, with_grant_option

revoke

privileges, on, from_role

object_type, schema, cascade

list_role_members

role

(none)

list_role_grants

role

(none)

db.maint.* / async_db.maint.*

Method

Positional args

Keyword-only params

size

(none)

pretty

table_size

table

schema, pretty

table_sizes

(none)

schema, limit

vacuum

(none)

table, schema, analyze, full

analyze

(none)

table, schema

explain

sql, params, analyze, format

(none — D2/D5 carve-out)

db.backup.* / async_db.backup.*

Method

Positional args

Keyword-only params

pg_dump

output_file

format, schema_only, data_only, tables, exclude_tables, schemas, compress, jobs

pg_restore

input_file

clean, if_exists, create, data_only, schema_only, tables, schemas, jobs, no_owner, no_privileges

copy_to_csv

table, output_file

schema, columns, delimiter, header, null_string, encoding

copy_from_csv

table, input_file

schema, columns, delimiter, header, null_string, encoding

db.etl.* / async_db.etl.*

Method

Positional args

Keyword-only params

init

(none)

(none)

history

name

limit

last_run

name

(none)

run

pipeline

dry_run

Supported Versions

Version floors are grounded in pyproject.toml constraints — no version is invented.

Component

Floor

Recommended

Python

3.11 (requires-python = ">=3.11")

3.12+

PostgreSQL

13

16+

PostGIS

3.0 (hard floor)

3.2+ (ST_MakeValid method=structure requires PostGIS 3.2, per D-02)

TimescaleDB

2.0

2.10+

psycopg

3.1.0 (psycopg>=3.1.0)

latest

psycopg_pool

3.2.0 (psycopg_pool>=3.2.0)

latest

pandas

2.0.0 (pandas>=2.0.0)

latest

geopandas

0.14.0 (geopandas>=0.14.0, optional geo extra)

latest

PostgreSQL floor note: pycopg is tested against PostgreSQL 13+. Features such as pg_dump --jobs require PostgreSQL 10+ (satisfied by the 13 floor).

PostGIS floor note: PostGIS 3.0 is the hard minimum — it provides ST_MakeValid and the GeoDataFrame geometry type support used by from_geodataframe / to_geodataframe. PostGIS 3.2+ is recommended for full ST_MakeValid method=structure support.

make_valid(method="structure") version note (D-04): ST_MakeValid(method="structure") requires PostGIS ≥ 3.2 / GEOS ≥ 3.10. On older servers, passing method="structure" raises a native PostGIS error — pycopg does not pre-check the server version. If you need to support PostGIS < 3.2, omit the method parameter (defaults to None, which calls ST_MakeValid without a method argument and works on all PostGIS 3.0+ servers).

TimescaleDB note: TimescaleDB feature availability depends on your edition:

  • Apache edition (free): time_bucket, chunk management, hypertables, compression, retention

  • TSL (Timescale Licensed): time_bucket_gapfill, continuous aggregate policies, reorder policies

Some methods raise FeatureNotSupported on the Apache edition — see the TimescaleDB docs for per-method edition requirements.

API Stability Tiers

API Surface

Stability

Notes

Database.* flat core

Stable

Frozen at 1.0

AsyncDatabase.* flat core

Stable

Full parity with sync

db.spatial.*

Stable

PostGIS helpers; requires [geo] extra

db.schema.*

Stable

DDL + introspection

db.timescale.*

Stable

TimescaleDB helpers

db.etl.*

Stable

ETL pipeline runner

db.admin.*

Stable

Role management

db.maint.*

Stable

Maintenance operations

db.backup.*

Stable

Backup/restore

Module-level build_*_sql builders

Stable

SQL fragment builders in pycopg.spatial

pycopg.exceptions.*Error

Stable

ExtensionNotAvailableError, TableNotFoundError, InvalidIdentifierError, DatabaseExistsError

Pipeline, RunResult

Stable

ETL data classes

Internal _* names

Private

No stability guarantee; may change at any time

For migration help when upgrading from earlier versions, see MIGRATION.md.