Last updated
One way: SharePoint stays the system of record, and SPSync keeps a read-only copy of your lists current in DuckDB and MotherDuck.
Status: Shipped · product: SPSync for Warehouses & Lakes
DuckDB runs inside the process that queries it, so a SharePoint list replicated to a DuckDB file is ready for analysis the moment the run finishes: open it from Python, R, the DuckDB CLI or a notebook, with nothing to install or administer. When more than one person needs the data, the same connection setting can point at MotherDuck instead.
File or MotherDuck
The destination takes one value, held in an environment variable:
- A file path, such as
/data/sharepoint.duckdb. SPSync creates the file on the first run. - A MotherDuck connection string,
md:followed by the database name and amotherduck_token. SPSync spots the prefix and writes to MotherDuck.
Tables go into DuckDB’s default main schema unless you name another, so a plain
duckdb.connect(path) sees them straight away.
The schema
A project list with a Client lookup, an Owner person column and a multi-choice Tags
column becomes pmo_projects, with client_id and client_value, an owner_user_id
column that joins to sp_users, and a pmo_projects__tags child table with one row per
tag. DuckDB does not enforce the joins between them, and none are declared, so rows can
arrive in any order. Types are DuckDB’s own: UUID, TIMESTAMPTZ, JSON.
Columns added in SharePoint show up after the next run. When a column’s type changes, a
_v2 column takes the new type and the original keeps its history.
Parquet from the same replication
A DuckDB destination can also export every table as Parquet each run: to a local folder, to Azure Blob or ADLS Gen2, or to Amazon S3 and S3-compatible stores. The Parquet page covers the layout. This is set in the command-line tool’s YAML config today.
How people use it
Recurring reports read the file in Power BI or Metabase without hitting the SharePoint list API on every refresh; see reporting and analytics and KPI dashboards.
Analytics engineers run dbt locally with DuckDB as a fast, disposable warehouse; see dbt on SharePoint.
Analysts who used to export lists by hand for Tableau, Looker or Metabase get one queryable file; see Tableau, Looker and Metabase on SharePoint lists.
Data engineers explore list data in DuckDB before writing it out to a lake; see SharePoint as a data lake.
Steps
- Sign in with delegated access, or have an admin grant
Sites.Selectedon the site. - Choose the lists or libraries.
- Set the file path or MotherDuck string.
- Run. The first run creates the file and loads every item; later runs touch only rows that changed.
- Open the file, or the MotherDuck database, in the tool you use.
A local DuckDB file takes one writer at a time, so leave writing to SPSync’s schedule and read from other processes. Copying the file is a complete backup. Neither DuckDB nor MotherDuck applies row-level security, so access to the file or database is the permission boundary.
Watch it run
Chapters are in the video's description.
Watch the walkthrough: the Northwind Analytics demo lists in SharePoint, connecting to Microsoft 365 in the desktop app, picking lists, the run into a local DuckDB file, and the replicated tables and rows.
Desktop, Azure or command line
All three editions run the same engine, write the same tables and use the same licence key, so the choice comes down to who looks after the sync.
Desktop app
For macOS and Windows. Sign in with Microsoft, tick the lists, choose a database and sync from your own machine on the schedule you set. Delegated sign-in needs no admin unless your tenant requires admin consent for apps.
In your Azure
SPSync in your Azure, a managed application for your own subscription, is deployed with you on a call while the Marketplace listing is prepared. Once deployed it is always on, so the sync keeps running with every laptop closed.
Command line
Every replication is a YAML file you can review and commit. Run it from a server, a container or a CI pipeline, and re-run it safely any time.
Frequently asked questions
Does SharePoint data arrive in one DuckDB file?
Yes, one file holding a table per list or library, plus child tables and the shared users table. Point SPSync at a MotherDuck database and the same tables are written there.
Can I query the replica from Python or a notebook?
Yes. The DuckDB CLI, Python's duckdb package, pandas, R and notebooks all read the file directly, with no server.
How do I switch from a local file to MotherDuck?
Put a MotherDuck connection string, md: followed by the database name and your token, where the file path would go. SPSync recognises the md: prefix and writes to MotherDuck with no other setting.
Can several people share one DuckDB replica?
A local file serves one machine at a time and allows one writer. For a copy that several people or tools query at once, use MotherDuck.
What happens to items deleted in SharePoint?
The row stays and its sp_deleted_at column is set, so a report can show deleted items or filter them out. To remove the row instead, set deletes: hard on that replication in the command-line tool's YAML config. Runs from the desktop app and from SPSync in your Azure always keep the row.
Talk to us about DuckDB and MotherDuck
SPSync is licensed per installation and per target family, with Standard Support included. Tell us which targets you need and how many machines, and we will size it with you. See how licensing works.
Set it up
Sign in to SharePoint with the SharePoint connection guide, then follow the DuckDB and the Parquet export setup guide to create the database user and open the connection for DuckDB and MotherDuck. If the connection test fails, the troubleshooting guide lists the errors people hit and what fixes them.
Script it
Everything you can click, you can script. The CLI, YAML config and automation below reproduce this scenario exactly. See the CLI reference.
spsync auth login --client-id 00000000-0000-0000-0000-000000000000
spsync export-config --output replication.yaml --pipeline-name duckdb --prefix duckdb --destination-kind duckdb --connection-string-env SPSYNC_DB_CONNECTION --dataset-name duckdb --client-id 00000000-0000-0000-0000-000000000000 --site-url https://contoso.sharepoint.com/sites/example --all-lists
spsync doctor replication.yaml
spsync run replication.yaml pipeline_name: duckdb
prefix: duckdb
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: duckdb
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: duckdb
scopes:
- site_url: https://contoso.sharepoint.com/sites/example
mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false
Cron (DuckDB nightly):
# every night at 02:00
0 2 * * * cd /opt/spsync && SPSYNC_DB_CONNECTION="$(cat /etc/spsync/db.env)" uv run spsync run replication.yaml >> /var/log/spsync/run.log 2>&1 GitHub Actions:
# .github/workflows/spsync-run.yml
name: SPSync
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch: {}
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv sync
- run: uv run spsync run replication.yaml
env:
SPSYNC_DB_CONNECTION: ${{ secrets.SPSYNC_DB_CONNECTION }}
SPSYNC_LICENCE_KEY: ${{ secrets.SPSYNC_LICENCE_KEY }} Azure DevOps:
# azure-pipelines.yml
schedules:
- cron: "0 2 * * *"
branches: { include: [main] }
always: true
steps:
- script: uv sync
- script: uv run spsync run replication.yaml
env:
SPSYNC_DB_CONNECTION: $(SPSYNC_DB_CONNECTION)
SPSYNC_LICENCE_KEY: $(SPSYNC_LICENCE_KEY) Windows (PowerShell + Task Scheduler):
# Windows Task Scheduler action (see /docs/cli/automation/)
$env:SPSYNC_DB_CONNECTION = (Get-Content C:\spsync\db.env)
uv run spsync run C:\spsync\replication.yaml
schtasks /Create /SC DAILY /ST 02:00 /TN "SPSync" /TR "powershell -File C:\spsync\run.ps1" macOS (launchd):
# ~/Library/LaunchAgents/io.spsync.run.plist, then: launchctl load ~/Library/LaunchAgents/io.spsync.run.plist Secrets (licence key, connection strings) come from the environment, so never write them into the YAML. See scheduling and secrets.