Last updated
None of these BI tools has a proper SharePoint list connector, so someone is stuck exporting CSVs by hand or wiring together a script just to get the data in.
Tableau, Looker, Metabase and Grafana connect to SQL databases and warehouses, and none of them has a proper native connector for a SharePoint list. A Metabase estate holding every department’s figures can still be missing one team’s account health scores because they live in a list, reaching the dashboard through a CSV exported on Mondays or a staging script that fails without warning whenever a column changes.
Put the list where the BI tool already looks
SPSync replicates the lists behind a dashboard into a database or warehouse, such as Postgres, BigQuery or Databricks, on a schedule, then keeps it current with delta reads. Each of these BI tools already connects well to that kind of destination. The dashboard work stays the same; the source underneath becomes a replication that runs by itself and follows the list when its columns change.
- 1ConnectSign in with Microsoft, or grant per-site app permissions.
- 2DiscoverPick sites, lists and libraries, or a whole site.
- 3MapSPSync reads each list's columns and creates the tables and keys.
- 4Full loadEvery item, paged the way Microsoft recommends, into the destination.
- 5Delta syncEvery few minutes only what changed moves: inserts, updates, deletes.
- 6ReconcileWeekly by default, or on demand with the command-line tool: a full comparison to confirm SharePoint and your database match, or to catch back up after an outage.
Steps 5 and 6 repeat for as long as the replication runs. Nothing is ever written to SharePoint.
A shape the semantic layer can model
Lookups are key columns and multi-value columns are child tables, the shape a semantic
layer expects for joins and aggregations. Person columns resolve into a shared sp_users
table, so an “account owner” dimension is defined once and reused across dashboards.
Deleted items keep their row with sp_deleted_at set, so a trend line over last year keeps
all its data points. SPSync can also generate a reporting view per list that adds person
names.
| Title | Client lookup | Tags multi-choice | Owner person | Due |
|---|---|---|---|---|
| Riverside | Acme Ltd | Design; Build | Ada Lovelace | 2026-11-30 |
| Harbour | Borealis plc | Build | Alan Turing | 2027-02-14 |
then sync
- id
- title
- client_id → clients.id
- owner_id → sp_users.id
- due
- is_deleted soft delete
- sp_modified
- id
- title
- …
- project_id
- tag
One row per value: Riverside has two.
- id
- display_name
One users table shared by every list.

Matching the destination to the tool
Tableau users coming from the retired SharePoint Lists connector will find the replacement, its sign-in options and its extract-only refresh in Tableau on SharePoint lists after the connector deprecation.
| Tool | A natural destination |
|---|---|
| Metabase, Looker | A warehouse, or Postgres |
| Tableau | SQL Server, Postgres or a warehouse |
| Grafana | Postgres or another SQL source |
BigQuery, Databricks and ClickHouse are shipped destinations today. Check the destination page for current status before you build a dashboard on one, and if the destination you need is missing, tell us; requests are what move a destination up the roadmap.
Include the lookup lists the model joins against when you choose the lists, and match the sync to how often the dashboard refreshes. Then connect the BI tool to the destination as it connects to any other source. If the tool’s semantic layer lives in a repository, keep the replication’s command-line config in the same repository and CI pipeline.
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.
Set it up
Ready to run this against your own site? The setup guide takes you from install to a first replication, and the SharePoint connection guide covers signing in.
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 tableau_looker_metabase --prefix tableau_looker_metabase --destination-kind postgres --connection-string-env SPSYNC_DB_CONNECTION --dataset-name tableau_looker_metabase --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: tableau_looker_metabase
prefix: tableau_looker_metabase
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: postgres
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: tableau_looker_metabase
scopes:
- site_url: https://contoso.sharepoint.com/sites/example
mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false
Cron (PostgreSQL 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.