★ New · v1.5 · DATA

REST APIs. SaaS apps. Any database. Into the map.

A Python dlt (dlthub.com) sidecar extracts data from REST APIs, 60+ verified sources, and any SQLAlchemy-supported database into per-profile staging DuckDB files — which the in-process DuckDB hub then ATTACHes read-only. This unlocks a source universe neither the DuckDB ATTACH cluster (Postgres/Mongo/Snowflake/…) nor the SharePoint / Google Sheets snapshot paths can reach.

REST API60+ verified sourcesSQLAlchemy databases Salesforce / HubSpot / Notion / GitHub / StripeIncremental loading6 geometry kindsAddress geocodingLoopback-only
How it works

Three source types, one staging contract

A long-lived Python sidecar (stdlib http.server — no FastAPI/uvicorn) runs pipelines on demand. Each profile writes to its own staging DuckDB file; the in-process hub ATTACHes it READ_ONLY and auto-discovers collections through every facade (FeatureServer / MVT / OGC). Refresh = re-run → file overwrites in place → next query sees new data.

  • REST API — declarative rest_api_source over any paged JSON endpoint. Pagination: none/offset/cursor/page/header_link. Auth: None/Bearer/ApiKey/Basic.
  • Verified Source — 60+ dlt sources pip-installed on demand: Salesforce, HubSpot, Notion, GitHub, Stripe, Airtable, Slack, Google Drive, Zendesk, Shopify, Asana…
  • SQL Databasesql_database over SQLAlchemy: postgresql / mysql / sqlite / mssql / snowflake / oracle / redshift. Especially useful for DBs with no DuckDB scanner (Oracle, SAP HANA, Redshift). Incremental via cursor column.
  • Geometry-awareAuto sniffs lat/lon then GeoJSON/geometry; plus LatLon, GeoJson, Wkt, Address (geocoded post-run → GPKG point layer), None.
POST /api/dlt/pipelines/{id}/run
# run a Salesforce pipeline
curl -X POST http://localhost:5059/api/dlt/pipelines/sfdc/run

→ sidecar dlt.pipeline(...).run(source)
→ writes <dataDir>/dlt/sfdc.duckdb
→ hub ATTACHes '...sfdc.duckdb' AS "sfdc" (READ_ONLY)
→ collections discovered → FeatureServer / MVT
→ geom auto-sniffed (lat/lon → ST_Point)
Address geocoding · incremental loading

From a JSON row to a point on the map

The Address geometry kind runs a C# post-pipeline geocoder over the staging rows — offline-first (OpenAddresses) with a Nominatim fallback, capped at 50 000 rows per run — and writes a dlt_<id>_geocoded GPKG point layer. Incremental loading persists the cursor value between runs so subsequent runs fetch only new rows.

🏠

Address → point

DltAddressGeocoder post-run step: reads the staging file, geocodes each row, writes a GPKG point layer. Offline-first.

🔁

Incremental loading

dlt persists the cursor in <dataDir>/dlt/<id>_state/; subsequent runs fetch only new rows.

🔒

Loopback-only by policy

The host refuses any bind wider than 127.0.0.1 / localhost / ::1; the sidecar additionally validates --host. Never a wider bind.

📦

Per-profile staging

Profile id == DuckDB schema alias == staging-file basename. Open() only ATTACHes if the file already exists — never spawns Python under the hub lock.

🐍

Python 3-tier locator

Dlt:PythonPath → bundled tools/python/python.exe → system PATH. Baseline: dlt>=1.4, duckdb>=0.10, pyarrow>=14.

🧩

Bookkeeping filtered

_dlt_* internal tables are filtered out of discovery — operators see only their data collections.

The API surface

Pipelines, status, sources

GroupEndpointNotes
RunPOST /api/dlt/pipelines/{id}/runTriggers dlt.pipeline(...).run(source) on the sidecar
StatusGET /api/dlt/pipelines/{id}/statusRunning / completed / failed + lastError
GeocodePOST /api/dlt/pipelines/{id}/geocodeExplicit address-geometry post-step → dlt_<id>_geocoded GPKG
SourcesGET /api/dlt/sources · /api/dlt/clientsVerified-source catalog + available DB clients
CancelPOST /api/dlt/pipelines/{id}/cancelCancel a running pipeline

Base URL http://localhost:5059; sidecar main port :8012 + health probe :8013. Config "Dlt": { Enabled: true, Host: "127.0.0.1", Port: 8012 }. Documented in DLT_GUIDE.md. Missing Python/dlt/duckdb/pyarrow → every route returns 503 + { error, installHint, available:false }; the sidecar is never auto-started at launch — only on explicit "Run Pipeline."

dlt is ELT, not reverse ETL — no write-back to source (write-back is a separate subsystem; see ServiceNow). Phase-3 roadmap: live progress SSE, scheduled/cron refresh, ADBC integration to surface the staging file to BI tools, and an MCP tool exposing run_pipeline behind the read-only invariant.

Sixty sources the desktop couldn't reach before

SaaS apps, REST APIs, and any SQLAlchemy DB — landing as served layers, geometry-aware, loopback-only.