SQLAlchemy with DuckDB and MotherDuck
SQLAlchemy is a SQL toolkit and Object-Relational Mapping (ORM) system for Python, providing full support for SQL expression language constructs and various database dialects. A lot of Business Intelligence tools supports SQLAlchemy out of the box.
SQLAlchemy is a Python SQL toolkit and Object-Relational Mapping (ORM) system that supports a wide range of database dialects. Many business intelligence tools support SQLAlchemy out of the box.
You can connect SQLAlchemy to MotherDuck through two paths:
- Recommended: MotherDuck's Postgres endpoint with SQLAlchemy's built-in PostgreSQL dialect and the
psycopgdriver. This path uses the standard PostgreSQL wire protocol and doesn't require DuckDB in your application environment. - DuckDB SQLAlchemy driver: the
duckdb-enginedialect, which connects through a DuckDB connection string. Use this path when you need DuckDB-specific SQLAlchemy behavior or local DuckDB features such as local-file access, local attachments, Dual Execution, or DuckDB extension management.
Why use the Postgres endpoint
The DuckDB SQLAlchemy driver can connect to MotherDuck through a DuckDB connection string, but most SQLAlchemy applications should use the Postgres endpoint instead:
- Standard connector support: Use SQLAlchemy's built-in PostgreSQL dialect with the
psycopgdriver. - No DuckDB dependency: Connect from serverless runtimes, containers, and application servers without bundling DuckDB.
- Production connection management: Use SQLAlchemy pooling with long-lived Postgres-compatible connections.
- Consistent integration path: Share the same connection parameters used by other Postgres-compatible tools.
Use the DuckDB SQLAlchemy driver only when your application needs local DuckDB features such as local-file access, local attachments, Dual Execution, or DuckDB extension management.
Before you start
You need a MotherDuck access token. Store it in an environment variable:
export MOTHERDUCK_TOKEN="your_token_here"
You also need your MotherDuck Postgres host. You can find it in MotherDuck Postgres settings. The examples below use pg.us-east-1-aws.motherduck.com; use the host shown for your account.
Install SQLAlchemy and psycopg
Install SQLAlchemy and the PostgreSQL connector:
pip install --upgrade sqlalchemy psycopg
Connect with SQLAlchemy
Create a SQLAlchemy engine with the PostgreSQL dialect and the psycopg driver:
import os
from sqlalchemy import create_engine, text
engine = create_engine(
"postgresql+psycopg://postgres@pg.us-east-1-aws.motherduck.com:5432/md:",
connect_args={
"password": os.environ["MOTHERDUCK_TOKEN"],
"sslmode": "verify-full",
"sslrootcert": "system",
},
pool_pre_ping=True,
)
with engine.connect() as conn:
result = conn.execute(text("SHOW DATABASES"))
for row in result:
print(row)
Using md: as the database name connects to your default database and uses workspace attach mode, which makes the databases in your MotherDuck workspace available from the session.
To connect to a specific database, replace md: with the database name:
engine = create_engine(
"postgresql+psycopg://postgres@pg.us-east-1-aws.motherduck.com:5432/sample_data",
connect_args={
"password": os.environ["MOTHERDUCK_TOKEN"],
"sslmode": "verify-full",
"sslrootcert": "system",
},
)
Query MotherDuck
Execute SQL with SQLAlchemy's text() construct:
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(
text(
"""
SELECT title, score
FROM sample_data.hn.hacker_news
WHERE type = 'story'
ORDER BY score DESC
LIMIT 5
"""
)
)
for row in result:
print(row.title, row.score)
The Postgres endpoint is a PostgreSQL-wire interface to MotherDuck. You write DuckDB SQL, not PostgreSQL SQL.
Loading data
For loading data through SQLAlchemy and the Postgres endpoint, prefer server-side reads from remote storage:
- Use
CREATE TABLE AS SELECTorINSERT INTO ... SELECT. - Point
read_parquet,read_csv, orread_jsonat S3, GCS, R2, Azure, or HTTPS. - Set
MD_RUN = REMOTEon remote file reads.
from sqlalchemy import text
with engine.begin() as conn:
conn.execute(
text(
"""
CREATE OR REPLACE TABLE my_db.main.weather_events AS
SELECT *
FROM read_csv(
'https://raw.githubusercontent.com/duckdb/duckdb-web/main/data/weather.csv',
HEADER = true,
AUTO_DETECT = true,
MD_RUN = REMOTE
)
"""
)
)
Use a DuckDB client path instead for local-file ingestion, COPY FROM STDIN, MD_RUN = LOCAL, or high-volume inserts from application memory.
Operational notes
- Use SSL: The Postgres endpoint requires encrypted connections.
sslmode=verify-fullwithsslrootcert=systemverifies the server certificate when supported by your client. - Keep tokens out of code: Pass your MotherDuck access token through an environment variable or secret manager.
- Prefer long-lived connections: Configure SQLAlchemy pooling for application workloads instead of opening a new connection per query.
- Avoid unsupported Postgres features: PostgreSQL-specific functions, indexes, sequences, stored procedures, and temporary tables are not supported.
Connecting with the DuckDB SQLAlchemy driver
If your application needs DuckDB-specific SQLAlchemy behavior or local DuckDB features, use the DuckDB SQLAlchemy driver and the DuckDB SQLAlchemy URI style instead.
Install the DuckDB SQLAlchemy driver
pip install --upgrade duckdb-engine
Connect to a local DuckDB database
Access a local DuckDB database with the SQLAlchemy URI:
duckdb:///path/to/file.db
Connect to MotherDuck
The general pattern for the SQLAlchemy URI to access a MotherDuck database is:
duckdb:///md:<my_database>?motherduck_token=<my_token>
The database name <my_database> in the connection string is optional. Omitting it lets you query multiple databases with one connection to MotherDuck.
You can authenticate in several ways:
1. Web login
If no token is available, the process directs you to a web login for authentication, which lets you obtain a token.
from sqlalchemy import create_engine, text
eng = create_engine("duckdb:///md:my_db")
with eng.connect() as conn:
result = conn.execute(text("SHOW DATABASES"))
for row in result:
print(row)
When you run the above, you'll see something like this to authenticate:

2. MOTHERDUCK_TOKEN environment variable
from sqlalchemy import create_engine, text
eng = create_engine("duckdb:///md:my_db")
with eng.connect() as conn:
result = conn.execute(text("SHOW DATABASES"))
for row in result:
print(row)
3. Configuration dictionary
from sqlalchemy import create_engine, text
config = {}
token = 'asdfwerasdf' # Fill in your token
config["motherduck_token"] = token
eng = create_engine(
"duckdb:///md:my_db",
connect_args={'config': config}
)
with eng.connect() as conn:
result = conn.execute(text("SHOW DATABASES"))
for row in result:
print(row)
4. Token as a connection string parameter
from sqlalchemy import create_engine, text
token = 'asdfwerasdf' # Fill in your token
eng = create_engine(f"duckdb:///md:my_db?motherduck_token={token}")
with eng.connect() as conn:
result = conn.execute(text("SHOW DATABASES"))
for row in result:
print(row)
The DuckDB Python API has a .sql() method on the connection API, but SQLAlchemy doesn't. Both share the .execute() function and concept. For more, see the SQLAlchemy connection documentation.
Related content
- Connect through the Postgres endpoint: Postgres endpoint guide
- Review connection parameters: Postgres Endpoint reference
- Connect from Python: Python through the Postgres endpoint
- Choose an interface: Client APIs