Introduction
In daily data analysis work, we often need to fetch data from external APIs—whether it’s JSON returned by RESTful APIs, CSV export files, or securely transmitted data over HTTPS connections. The traditional approach involves writing scripts to download data first, saving it locally, and then loading it into an analysis tool. DuckDB’s `httpfs` extension completely transforms this workflow, allowing us to read remote files directly from URLs, achieving a seamless “zero intermediate storage” analysis pipeline.
This article demonstrates how to efficiently ingest HTTPS/API data using DuckDB’s httpfs extension through real-world business scenarios, including JSON parsing, incremental pulls, and performance optimization techniques.

Figure: DuckDB httpfs data ingestion architecture—from HTTPS sources to local analysis
Environment Setup
Ensure your DuckDB version is >= 0.9.0 (latest stable recommended), and load the httpfs extension:
INSTALL httpfs;
LOAD httpfs;
Verify installation:
SELECT * FROM extensions WHERE name = 'httpfs';
Scenario 1: Direct Remote JSON API Reading
1.1 Basic Reading
Suppose we want to fetch the latest commit records of a repository from the GitHub API:
-- Read JSON directly from GitHub API
CREATE TABLE github_commits AS
SELECT * FROM read_json_auto(
'https://api.github.com/repos/duckdb/duckdb/commits?per_page=5'
);
SELECT commit->>'sha' AS sha,
commit->>'message' AS message,
author->>'name' AS author_name
FROM github_commits
LIMIT 5;
1.2 Handling Nested JSON
GitHub API returns nested structures that `read_json_auto` automatically flattens. For finer control, use `json_extract`:
-- Manually parse nested fields
SELECT
json_extract(commit, '$.sha') AS commit_sha,
json_extract(commit, '$.commit.author.name') AS author_name,
json_extract(commit, '$.commit.author.date') AS commit_date,
json_extract(commit, '$.commit.stats.total') AS total_changes
FROM github_commits
LIMIT 3;
Sample output:
| commit_sha | author_name | commit_date | total_changes |
|---|---|---|---|
| a1b2c3d… | DuckDB Dev | 2026-07-15 | 142 |
| e4f5g6h… | DuckDB Dev | 2026-07-14 | 89 |
| i7j8k9l… | Community | 2026-07-13 | 56 |

Figure: DuckDB CLI querying GitHub API JSON data directly
Scenario 2: HTTPS Authentication & Custom Headers
Many APIs require authentication. httpfs supports passing auth info via `user_agent` and custom headers:
-- Access private API with Bearer Token
CREATE TABLE private_data AS
SELECT * FROM read_json_auto(
'https://api.example.com/v1/data',
type_map='auto',
file_format='json',
headers='Authorization: Bearer MY_TOKEN_...'
);
2.1 Setting User-Agent
Some APIs reject requests without a User-Agent header:
-- Set global HTTP configuration
PRAGMA http_user_agent('DuckDB-DataBot/1.0');
-- Then read normally
SELECT * FROM read_csv_auto(
'https://data.example.com/export.csv'
);
2.2 Handling Paginated APIs
For APIs returning large datasets, pagination is typically required. Here’s an incremental pull pattern:
-- Create target table
CREATE TABLE api_incremental (
id INTEGER,
name VARCHAR,
value DOUBLE,
fetched_at TIMESTAMP
);
-- Simulate paginated pull (replace with real API in production)
WITH RECURSIVE page_counter(page) AS (
VALUES (0)
UNION ALL
SELECT page + 1 FROM page_counter WHERE page < 4
)
INSERT INTO api_incremental
SELECT
page * 100 + row_number() OVER (ORDER BY (SELECT NULL)) AS id,
'item_' || page * 100 + row_number() OVER (ORDER BY (SELECT NULL)) AS name,
random() * 1000 AS value,
current_timestamp AS fetched_at
FROM page_counter, generate_series(1, 100);
SELECT COUNT(*) AS total_records FROM api_incremental;
Scenario 3: Reading Remote CSV/Parquet Files
httpfs supports not only JSON but also CSV and Parquet formats:
-- Read remote CSV directly
CREATE TABLE remote_csv AS
SELECT * FROM read_csv_auto(
'https://raw.githubusercontent.com/duckdb/duckdb/main/extension/httpfs/test/data/sample.csv'
);
-- Quick overview
DESCRIBE remote_csv;
-- Aggregate on remote Parquet without downloading
SELECT
region,
COUNT(*) AS order_count,
SUM(total_amount) AS total_revenue
FROM read_parquet(
'https://example-data.com/sales/*.parquet'
)
GROUP BY region
ORDER BY total_revenue DESC;
3.1 Batch Multi-file Reading
Supports wildcards and directory patterns:
-- Read all Parquet files in a directory
SELECT * FROM read_parquet('https://storage.example.com/daily_reports/2026-07-*.parquet');
-- Merge multiple CSVs
SELECT * FROM read_csv_auto(
'https://example.com/exports/jan.csv',
'https://example.com/exports/feb.csv',
'https://example.com/exports/mar.csv'
);
Scenario 4: Incremental Pull & Deduplication
In real business scenarios, API data is often incrementally updated. Here’s a complete incremental pull pattern:
-- 1. Create staging table with metadata
CREATE TABLE IF NOT EXISTS api_staging (
record_id VARCHAR PRIMARY KEY,
data JSON,
source_url VARCHAR,
fetched_at TIMESTAMP DEFAULT current_timestamp,
is_deleted BOOLEAN DEFAULT FALSE
);
-- 2. Check last fetch time
SELECT MAX(fetched_at) AS last_fetch FROM api_staging;
-- 3. Pull only updated data (using If-Modified-Since header)
INSERT INTO api_staging
SELECT * FROM read_json_auto(
'https://api.example.com/data?since=2026-07-16T00:00:00Z',
headers='If-Modified-Since: Fri, 16 Jul 2026 00:00:00 GMT'
);
-- 4. Deduplicate: keep latest version
DELETE FROM api_staging
WHERE record_id IN (
SELECT record_id
FROM api_staging
GROUP BY record_id
HAVING COUNT(*) > 1
)
AND rowid NOT IN (
SELECT MIN(rowid)
FROM api_staging
GROUP BY record_id
);
Performance Optimization Tips
4.1 Concurrent Connections
-- Set maximum HTTP connections
PRAGMA http_max_connections = 10;
-- Set timeout (seconds)
PRAGMA http_timeout = 30;
4.2 Caching Strategy
For frequently accessed API endpoints, combine with local file caching:
-- First pull and cache
COPY (
SELECT * FROM read_json_auto('https://api.example.com/heavy-endpoint')
) TO '/tmp/cache/heavy_endpoint.json';
-- Subsequent reads from local cache
SELECT * FROM read_json_auto('/tmp/cache/heavy_endpoint.json');
4.3 Streaming Large Files
-- Use stream mode to avoid memory overflow
SELECT * FROM read_csv_auto(
'https://example.com/large_export.csv',
stream=true
);
Complete Practical Example: E-commerce Order Data Ingestion
Here’s a complete example of ingesting e-commerce order data from an API:
-- Create orders table
CREATE TABLE orders (
order_id VARCHAR,
customer_id VARCHAR,
product_name VARCHAR,
quantity INTEGER,
unit_price DECIMAL(10,2),
order_date DATE,
status VARCHAR,
region VARCHAR
);
-- Ingest from API (simulated)
INSERT INTO orders
SELECT
json_extract(item, '$.order_id') AS order_id,
json_extract(item, '$.customer_id') AS customer_id,
json_extract(item, '$.product_name') AS product_name,
cast(json_extract(item, '$.quantity') AS INTEGER) AS quantity,
cast(json_extract(item, '$.unit_price') AS DECIMAL(10,2)) AS unit_price,
cast(json_extract(item, '$.order_date') AS DATE) AS order_date,
json_extract(item, '$.status') AS status,
json_extract(item, '$.region') AS region
FROM read_json_auto(
'https://shop.example.com/api/v2/orders?date_from=2026-01-01&limit=10000',
type_map='auto'
);
-- Analysis: revenue by region
SELECT
region,
COUNT(*) AS order_count,
SUM(quantity * unit_price) AS revenue,
AVG(unit_price) AS avg_price
FROM orders
WHERE status = 'completed'
GROUP BY region
ORDER BY revenue DESC;
Summary
DuckDB’s httpfs extension provides a “highway” for data analysts to reach remote data:
- Zero Intermediate Storage: Read directly from URLs without pre-downloading
- Multi-format Support: JSON, CSV, and Parquet all queryable directly
- Flexible Authentication: Supports Bearer Tokens and custom headers
- Streaming Processing: Large files won’t overflow memory
- Incremental Friendly: Easy incremental pulls with timestamps and dedup logic
With these skills, you can turn DuckDB into a powerful “data connector,” making your analysis workflow smoother and more efficient.
More DuckDB in-action tips, follow DuckDB Lab (duckdblab.org).