HTTP(s) Data Connector
Query files and JSON APIs over HTTP or HTTPS with SQL, with authentication, pagination, response caching, and dynamic filters.
The HTTP(s) Data Connector enables federated SQL query across supported file formats stored at an HTTP(s) endpoint. The connector supports dynamic query and data refresh through SQL-based filtering.
datasets:
- from: http://static_username@localhost:3001/report.csv
name: local_report
params:
http_password: ${env:MY_HTTP_PASS}Examples
Basic Example
datasets:
- from: https://github.com/LAION-AI/audio-dataset/raw/7fd6ae3cfd7cde619f6bed817da7aa2202a5bc28/metadata/freesound/parquet/freesound_parquet.parquet
name: laion_freesoundUsing Basic Authentication
datasets:
- from: http://static_username@localhost:3001/report.csv
name: local_report
params:
http_password: ${env:MY_HTTP_PASS}The username is taken from the user-info section of the from URL (user@host) or from the http_username parameter. The password comes from the http_password parameter. The connector then sends a standard RFC 7617 Basic authentication header on every request:
Authorization: Basic <base64(username:password)>For example, static_username with password s3cret produces Authorization: Basic c3RhdGljX3VzZXJuYW1lOnMzY3JldA==. Only one of http_password or user info in the URL can provide the password: setting both is not supported.
Prefer http_password with a secret store over a password in the URL. For a dynamic JSON API endpoint, query errors and endpoint-validation warnings name only the origin of the request URL (scheme, host, and port), for example HTTP request to http://127.0.0.1:8080 failed: error sending request, so its user info, path, and query string stay out of them. The dataset registration log line, and errors from a structured HTTP file dataset, can print the configured from URL with its user info.
Using Custom Headers
Custom HTTP headers can be specified for authentication, API keys, or other requirements. Headers are treated as sensitive data and will not be logged.
http_headers applies to dynamic JSON API endpoints only. A structured HTTP file dataset (csv, tsv, parquet, arrow, avro, jsonl/ndjson/ldjson, soda, socrata, vortex, or a static json file) is served by the object-store listing path, which cannot carry request headers, so the headers are ignored and a warning is logged naming the dataset. To authenticate a structured file download, use Basic authentication (http_username / http_password, or user info in the URL).
datasets:
- from: https://api.example.com
name: api_data
params:
http_headers: 'Authorization:Bearer ${secrets:api_token},Accept:application/json'Headers can also be separated by semicolons:
datasets:
- from: https://api.example.com
name: api_data
params:
http_headers: 'Authorization: Bearer ${secrets:api_token}; X-API-Key: ${secrets:api_key}'Using OAuth2 Refresh-Token Authentication
For JSON APIs protected by OAuth2, the connector can acquire short-lived access tokens from a token endpoint and keep them fresh automatically. The refresh-token grant (RFC 6749 §6) exchanges a long-lived refresh token; the client-credentials grant (RFC 6749 §4.4) authenticates with a client_id/client_secret for machine-to-machine APIs. On startup Spice hits the configured token endpoint once, then stamps Authorization: Bearer <access_token> on every data request (or a custom header: see Custom Token Header) and refreshes the token in the background before it expires.
datasets:
- from: https://api.example.com
name: secure_data
params:
file_format: json
allowed_request_paths: '/v1/**'
auth_token_url: https://auth.example.com/oauth/token
http_auth_refresh_token: ${secrets:my_refresh_token}
http_auth_client_id: ${secrets:my_client_id}
http_auth_client_secret: ${secrets:my_client_secret}
auth_scopes: 'read:data offline_access'The http_auth_refresh_token, http_auth_client_id, and http_auth_client_secret parameters can be loaded from any supported secret store (environment variables, Kubernetes Secrets, AWS Secrets Manager, HashiCorp Vault, the OS keychain, etc.) using the ${secrets:...} replacement syntax.
Applies to dynamic JSON API endpoints only (e.g. file_format: json with allowed_request_paths). A structured HTTP file dataset (csv/parquet/etc.) goes through the object-store listing path, which cannot attach an access token, so setting any OAuth2 parameter on one is rejected at registration: the dataset fails to load with a configuration error naming the parameters to remove, rather than quietly sending unauthenticated requests. http_headers is not applied on that path either; use Basic authentication to authenticate a structured file download.
See OAuth2 Authentication for the full parameter reference and behavior notes.
Configuration
from
The from field specifies the HTTP(s) endpoint and can be configured in two ways:
-
Direct URL to a file: A complete URL pointing to a specific supported file.
from: https://example.com/data/report.csv -
Base domain/path: A base URL that will be combined with special metadata fields to construct the complete request.
from: https://api.example.com/v1
The connector supports templated URLs with query parameters that can be dynamically populated using refresh_sql filters and special metadata fields.
name
The dataset name. This will be used as the table name within Spice.
Example:
datasets:
- from: http://static_username@localhost:3001/report.csv
name: cool_dataset
params: ...SELECT COUNT(*) FROM cool_dataset;+----------+
| count(*) |
+----------+
| 6001215 |
+----------+The dataset name cannot be a reserved keyword.
params
The connector supports authentication, timeout, connection pooling, and retry configuration via params.
| Parameter Name | Description |
|---|---|
http_port | Optional. Port to create HTTP(s) connection over. Default: 80 and 443 for HTTP and HTTPS respectively. |
http_username | Optional. Username for HTTP basic authentication. Default: None. |
http_password | Optional. Password for HTTP basic authentication. Default: None. Use the secret replacement syntax to load the password from a secret store, e.g. ${secrets:my_http_pass}. |
http_headers | Optional. Custom HTTP headers as a comma-separated list of key:value pairs. Example: Content-Type:application/json,Accept:application/json. Applies to dynamic JSON API endpoints only; structured HTTP file datasets ignore these headers. Default: None. |
allowed_request_paths | Required for using request_path filters. Comma-separated list of allowed paths. Example: /api/users,/api/posts. Entries must start with /. Each request_path value is also checked before matching; see Request Path Validation. |
request_query_filters | Optional. Set to enabled to enable request_query filters. Default: disabled. When disabled, query parameter filters will be rejected. |
request_body_filters | Optional. Set to enabled to enable request_body filters for POST requests. Default: disabled. When disabled, request body filters will be rejected. |
client_timeout | Optional. Maximum time to wait for a response from the HTTP server (in seconds). Default: 30. Applied to the entire request-response cycle. |
connect_timeout | Optional. Timeout for establishing HTTP(s) connections (in seconds). Default: 10. |
pool_max_idle_per_host | Optional. Maximum number of idle connections to keep alive per host. Default: 10. |
pool_idle_timeout | Optional. Timeout for idle connections in the pool (in seconds). Default: 90. |
max_retries | Optional. Maximum number of retries for failed HTTP requests. Default: 3. |
retry_backoff_method | Optional. Retry backoff strategy: fibonacci (default), linear, or exponential. |
retry_max_duration | Optional. Maximum total duration for all retries (e.g., 30s, 5m). If not set, retries continue up to max_retries. |
retry_jitter | Optional. Randomization factor for retry delays (0.0 to 1.0). Default: 0.3 (30% randomization). Set to 0 for no jitter. |
on_error_response | Optional. What a response outside 2xx becomes: error fails the request, warn records a client error's response as a row and logs a warning, and store records a client error's response as a row without a warning. warn and store apply only to a 4xx other than 429. Default: error. See Error Responses. |
max_request_query_length | Optional. Maximum length in characters for request_query filter values. Default: 1024. Maximum: 4096. |
max_request_body_bytes | Optional. Maximum size in bytes for request_body filter values. Default: 16384 (16 KiB). Maximum: 65536 (64 KiB). |
request_header_filters | Optional. Set to enabled to allow request_headers filters to push down dynamic HTTP request headers. Default: disabled. Requires request_header_allowlist. |
request_header_allowlist | Comma-separated list of HTTP header names that request_headers filters may set (e.g., x-sandbox-id, x-region). Required when request_header_filters is enabled. The authorization header cannot be allowlisted when HTTP authentication is configured. |
max_request_headers_length | Optional. Maximum size in bytes for request_headers filter values. Default: 16384 (16 KiB). |
max_request_partitions | Optional. Maximum number of HTTP request partitions created from the cross product of request_path, request_query, request_body, and request_headers filters. If unset, partition count is unlimited. |
response_cache_max_size_bytes | Optional. Byte budget for the responses this dataset retains in its response cache, counting response bodies and the request keys they are held under. Once reached, entries are evicted to stay inside it. Set 0 to disable the response cache. Default: 67108864 (64 MiB), applied per dataset. Applies to dynamic JSON API endpoints only; structured HTTP file datasets do not use this cache. |
response_cache_fallback_ttl | Optional. How long to retain a response whose origin sent no Cache-Control header at all (e.g. 5m, 30s). An origin that did send Cache-Control is always honored instead, including its refusals. Unset by default, which keeps such responses uncached. |
health_probe | Optional. Custom health probe path for endpoint validation during initialization (e.g., /health, /api/status). The endpoint must return a 2xx status code to pass validation. If not set, a random path is used and any status (including 404) is accepted. Must start with /. |
auth_token_url | Optional. OAuth2 token endpoint URL (must be HTTPS; http://localhost and loopback IPs are allowed for local testing). Enables OAuth2: the connector acquires short-lived access tokens (refresh-token grant by default, or client_credentials via auth_grant_type) and attaches them to all data requests (Authorization: Bearer <token> by default, or the bare token under a custom auth_header_name). Applies to dynamic JSON API endpoints only; structured HTTP file datasets reject OAuth2 params. See OAuth2 Authentication. |
auth_grant_type | Optional. OAuth2 grant type: refresh_token (default, RFC 6749 §6) or client_credentials (RFC 6749 §4.4). client_credentials authenticates with http_auth_client_id/http_auth_client_secret and issues no refresh token. |
http_auth_refresh_token | Optional. OAuth2 refresh token exchanged against auth_token_url to obtain access tokens. Required when auth_token_url is set with the (default) refresh-token grant; not used by the client_credentials grant. Use a secret store, e.g. ${secrets:my_refresh_token}. |
http_auth_client_id | Optional. OAuth2 client_id presented to the token endpoint. Required for confidential clients; optional for public clients. Must be paired with http_auth_client_secret for confidential clients. |
http_auth_client_secret | Optional. OAuth2 client_secret presented to the token endpoint. Required when the client is confidential; must be set together with http_auth_client_id. Use a secret store, e.g. ${secrets:my_client_secret}. |
auth_scopes | Optional. Space-separated OAuth2 scopes to request when refreshing (e.g. read:data offline_access). Omit to inherit the scopes bound to the refresh token. |
auth_client_auth | Optional. How client credentials are sent to the token endpoint: basic (HTTP Basic header, default per RFC 6749 §2.3.1) or body (client_id/client_secret in the form body). Default: basic. |
auth_header_name | Optional. HTTP header that carries the access token. Default: Authorization (sends Bearer <token>). Any other name (e.g. X-Shopify-Access-Token) sends the bare token instead. |
Mutual TLS (mTLS) Client Authentication
For upstream servers that require mutual TLS, the connector can present a client certificate during the TLS handshake. Provide the certificate and key either as file paths or inline PEM: the file-path and inline forms are mutually exclusive, and the certificate and key must always be set together. mTLS client identity applies to dynamic JSON API endpoints only; structured HTTP file datasets reject these parameters.
| Parameter Name | Description |
|---|---|
http_tls_client_certificate_file | Optional. Path to a PEM client certificate chain to present during the TLS handshake. Must be set together with http_tls_client_key_file. Mutually exclusive with the inline forms. |
http_tls_client_key_file | Optional. Path to the PEM private key matching http_tls_client_certificate_file. Must be set together with it. Mutually exclusive with the inline forms. |
http_tls_client_certificate | Optional. Inline PEM client certificate chain (or ${secrets:...} reference). Must be set together with http_tls_client_key. Mutually exclusive with the file-path forms. |
http_tls_client_key | Optional. Inline PEM private key (or ${secrets:...} reference) matching http_tls_client_certificate. Must be set together with it. Mutually exclusive with the file-path forms. |
Rate Control Parameters
HTTP-based connectors share a rate control system that limits concurrency and request rate per upstream origin. These parameters can be set per-dataset (in params) or globally (in runtime.params). Dataset-level settings override the global defaults.
| Parameter Name | Description |
|---|---|
max_concurrent_requests | Maximum number of concurrent HTTP requests to the same upstream origin. Overrides runtime.params.http_max_concurrent_requests. If both are unset, concurrency limiting is disabled. |
requests_per_second_limit | Maximum number of HTTP requests per second to the same upstream origin. Overrides runtime.params.http_requests_per_second_limit. If both are unset, no per-second rate limit is applied. |
requests_per_minute_limit | Maximum number of HTTP requests per minute to the same upstream origin. Overrides runtime.params.http_requests_per_minute_limit. If both are unset, no per-minute rate limit is applied. |
rate_control_jitter_min | Minimum random delay added before HTTP requests when rate control is active. Accepts durations such as 5ms or 0ms. Defaults to 5ms when a request-rate limit is configured. |
rate_control_jitter_max | Maximum random delay added before HTTP requests when rate control is active. Accepts durations such as 10ms or 0ms. Defaults to 10ms when a request-rate limit is configured. |
rate_control_acquire_timeout | Maximum time a request waits for rate-control capacity before it fails, as a duration such as 30s. Overrides runtime.params.http_rate_control_acquire_timeout. Defaults to client_timeout. 0 waits without a bound. |
rate_control_failure_threshold | Upstream error rate above which adaptive rate control admits fewer requests than the configured limits, as a percentage (25%) or a fraction (0.25). Overrides runtime.params.http_rate_control_failure_threshold. Default: 10%. |
rate_control_window | Reaction and recovery window of adaptive rate control, as a duration. A shorter window reacts and recovers faster. Overrides runtime.params.http_rate_control_window. Default: 10s. With cluster rate control, the default is runtime.source_rate_control.refresh_interval, and a shorter window is raised to it. |
Multiple datasets targeting the same origin share the same rate controller, ensuring the limits apply across all datasets for that origin. Every dataset that shares an origin must resolve to the same values for these parameters; a dataset whose values differ fails to load. See HTTP Rate Control for adaptive throttling, bounded waits, and limits across instances.
runtime:
params:
http_max_concurrent_requests: 10
http_requests_per_second_limit: 5
datasets:
- from: https://api.example.com/v1
name: api_data
params:
file_format: json
allowed_request_paths: '/data/**'
max_concurrent_requests: 3 # Override: this dataset uses at most 3 concurrent requestsPagination Parameters
| Parameter Name | Description |
|---|---|
pagination | Optional. Pagination mode: auto (default) auto-detects Link headers, enabled explicitly enables pagination with configuration below, disabled turns off pagination. |
pagination_next_pointer | Optional. JSON pointer (RFC 6901) to the next page URL or cursor in the response body (e.g., /next, /pagination/cursor, /links/next). |
pagination_link_header | Optional. Whether to follow HTTP Link headers with rel="next" for pagination. Default: enabled. Set to disabled to ignore Link headers. |
pagination_token_param | Optional. When set, the value from pagination_next_pointer is treated as a cursor/token and passed as this query parameter name in subsequent requests. When not set, the value is treated as a full URL. |
pagination_data_pointer | Optional. JSON pointer (RFC 6901) to the data array in each page's response (e.g., /data, /results, /items). When set, only the array at this path is returned as data rows. |
pagination_max_pages | Optional. Maximum number of pages to fetch. Default: 100. Set to nolimit to disable the page cap and fetch all available pages. |
pagination_data_map_to_array | Optional. When enabled, if the data at pagination_data_pointer (or the top-level response) is a JSON object/map, extracts its values as rows instead of treating it as a single row. Default: disabled. Requires pagination to be enabled. |
pagination_query_params | Optional. Query parameter template for client-driven pagination. Supports {offset}, {limit}, and {page} variables (e.g., offset={offset}&limit={limit}). Requires pagination_page_size. Mutually exclusive with pagination_next_pointer and pagination_token_param. |
pagination_page_size | Optional. Number of items per page for query-parameter pagination. Must be a positive integer. Expands {limit} in pagination_query_params and detects the last page (fewer results than page_size means done). Requires pagination_query_params. |
Caching Mode Parameters
When using refresh_mode: caching, cache freshness is controlled by additional parameters placed under acceleration.params: not under the top-level params block.
| Parameter | Description | Default |
|---|---|---|
caching_ttl | How long cached data is considered fresh. After this period, data becomes stale and a background refresh is triggered. Defaults to 30s. | 30s |
caching_stale_while_revalidate_ttl | How long after caching_ttl expires to continue serving stale data while a background refresh runs. If omitted, queries wait for fresh data once TTL expires. | None |
caching_stale_if_error | Serves expired cached data if the upstream source returns an error rather than failing the query. A duration (600s) bounds how stale an entry may be past caching_ttl and still be served; enabled is unbounded, disabled never serves stale. | disabled |
caching_max_size | Byte budget for what the acceleration stores, e.g. 512MiB. Without it (or caching_max_items) nothing caps how much the cache holds. | None |
caching_max_items | Maximum number of rows the acceleration may keep, e.g. 100000. | None |
To prefer the origin and fall back to the accelerator only when the origin fails, set caching_ttl: 0s, caching_stale_while_revalidate_ttl: 0s, and caching_stale_if_error to enabled or a finite duration. While the origin is healthy, Spice does not serve the accelerator. A successful response is still stored and is consulted only after a later origin failure. A zero TTL does not mean every read is served from the accelerator. Pair unbounded enabled with caching_max_size, caching_max_items, or retention. See Prefer the origin, fall back on failure.
datasets:
- from: https://api.example.com/v1
name: api_cache
params:
file_format: json
allowed_request_paths: '/data/**'
request_query_filters: enabled
acceleration:
enabled: true
refresh_mode: caching
refresh_check_interval: 15m
on_zero_results: use_source
params:
caching_ttl: 15m # explicit: default is only 30s
caching_stale_while_revalidate_ttl: 5m # serve stale data while refreshingSee Caching Refresh Mode for full TTL semantics, stale-while-revalidate behaviour, and cache persistence options.
HTTP Response Headers
When querying HTTP(s) datasets, Spice respects standard HTTP caching headers in responses. The connector supports the following cache-related response headers:
Cache-Control
The Cache-Control response header from the HTTP(s) endpoint is passed through to clients querying Spice. When the HTTP(s) server returns a Cache-Control header with the stale-while-revalidate directive, clients can use this value to determine appropriate caching behavior.
For example, if the HTTP(s) endpoint returns:
Cache-Control: max-age=10, stale-while-revalidate=10
Clients querying Spice will receive this header and can:
- Serve fresh data for 10 seconds after fetching.
- Between 10-20 seconds, serve stale data while fetching fresh data in the background.
- After 20 seconds, fetch fresh data before serving the next request.
The stale-while-revalidate behavior in Spice is controlled by the stale_while_revalidate_ttl parameter in the caching configuration. When stale_while_revalidate_ttl is set to 0 (default), stale data will not be served. When set to a non-zero value, Spice serves stale cache entries while revalidating in the background.
Response Cache
For dynamic JSON API endpoints, the connector keeps origin responses in an in-memory cache keyed by the request shape (path, query, body, and request headers) so a repeat of the same request inside the response's own freshness window is served without another origin call. This cache is the connector's own; it is not one of the caches configured under runtime.caching, and it is unrelated to refresh_mode: caching, which caches query results rather than HTTP responses. Structured HTTP file datasets (parquet, csv, and the other listing-table formats) do not use it.
Size budget
response_cache_max_size_bytes bounds what one dataset's cache retains, counting each response body together with the request key it is held under. It defaults to 67108864 (64 MiB) and is applied per dataset, so the runtime's total exposure grows with the number of HTTP datasets: raise it only for a dataset that earns it. Once the budget is reached, entries are evicted to stay inside it. Set 0 to disable the cache entirely.
The value must be a whole number of bytes; suffixed forms such as 64MiB are rejected at load with an error naming the parameter, rather than being replaced by the default.
A single entry (one response plus the request key it is held under) of 4 GiB or more is never admitted, whatever the budget is set to, because it cannot be charged what it holds.
What is retained, and for how long
The origin decides first, from the Cache-Control header on its response:
| Origin response | Retained |
|---|---|
Cache-Control: max-age=<n> (n > 0) | Yes, for the part of <n> that has not already elapsed according to the response's Age / Date headers. |
Cache-Control: s-maxage=<n> | Yes, for <n>. s-maxage is addressed to shared caches and outranks a max-age sent alongside it. |
Cache-Control: no-store, no-cache, or private | No. The response is still served to the query, but never retained: even if max-age was sent with it. |
Cache-Control with no usable max-age/s-maxage | No. The origin has spoken, so response_cache_fallback_ttl does not step in. |
No Cache-Control header at all | Only if response_cache_fallback_ttl is set. See below. |
A Vary response header additionally refuses retention when it is *, when it names the header the connector's HTTP authentication uses, or when its bytes cannot be read as text. Any other named field is safe, because everything a query can vary (path, query, body, and pushed-down request headers) is already part of the cache key.
Caching an origin that sends no Cache-Control
response_cache_fallback_ttl is how long to retain a response whose origin sent no Cache-Control header at all, for example:
datasets:
- from: https://api.example.com/v1
name: api_data
params:
file_format: json
response_cache_fallback_ttl: 5m
response_cache_max_size_bytes: 134217728 # 128 MiBIt is unset by default, so responses from a header-less origin stay uncached until an operator asks for them to be retained. It never overrides an origin that did send Cache-Control, including that origin's refusals, and unlike an origin-supplied max-age it is not reduced by the response's Age: it is how long you asked Spice to keep a response the origin said nothing about.
Occupancy of the cache is reported per dataset: see Metrics.
Advanced Features
The HTTP connector provides advanced capabilities for working with dynamic APIs and RESTful services, including built-in pagination and special metadata fields.
Pagination
The HTTP connector supports automatic pagination for REST APIs that return data across multiple pages. Pagination is configured via params and works transparently with acceleration (caching, append, and full refresh modes): each page is streamed as a separate batch without buffering entire result sets in memory.
Pagination Modes
There are three pagination modes:
URL mode: The next page URL is extracted from the response body (via pagination_next_pointer) or from the HTTP Link header with rel="next".
datasets:
- from: https://api.example.com/v1/items
name: items
params:
pagination: enabled
pagination_next_pointer: /links/next
pagination_data_pointer: /data
pagination_max_pages: 50Token mode: A cursor/token is extracted from the response body (via pagination_next_pointer) and passed as a query parameter (specified by pagination_token_param) in subsequent requests.
datasets:
- from: https://api.example.com/v1/items
name: items
params:
pagination: enabled
pagination_next_pointer: /pagination/cursor
pagination_token_param: cursor
pagination_data_pointer: /resultsQuery-parameter mode: The client drives pagination by expanding a template (pagination_query_params) with {offset}, {limit}, and {page} variables. Pagination stops when a page returns fewer rows than pagination_page_size.
datasets:
- from: https://api.example.com/v1/widgets
name: widgets
params:
pagination: enabled
pagination_query_params: "offset={offset}&limit={limit}"
pagination_page_size: "100"
pagination_max_pages: "50"Map-to-Array Conversion
Some APIs return data as a JSON object/map (e.g., {"1": {...}, "2": {...}}) instead of an array. Set pagination_data_map_to_array: enabled to extract the map values as individual rows.
datasets:
- from: https://api.example.com/v1/records
name: records
params:
pagination: enabled
pagination_data_map_to_array: enabled
pagination_query_params: "offset={offset}&limit={limit}"
pagination_page_size: "100"Auto Mode
By default, pagination is set to auto, which automatically follows HTTP Link headers with rel="next" if present in responses. Set pagination: disabled to turn off all pagination behavior, or pagination: enabled to explicitly configure pagination with the parameters above.
Validation Rules
pagination_query_paramsrequirespagination_page_size(and vice versa)pagination_query_paramsis mutually exclusive withpagination_next_pointerandpagination_token_parampagination_query_paramsmust contain{offset}or{page}to ensure pages advancepagination_token_paramrequirespagination_next_pointerpagination_next_pointerandpagination_data_pointermust be valid JSON pointers starting with/
SSRF Protection
When using URL mode, next-page URLs extracted from response bodies are validated to share the same origin as the base URL configured in from. Cross-origin redirects are rejected.
Special Metadata Fields
The HTTP connector supports special metadata fields that provide fine-grained control over HTTP requests. These fields can be included in your dataset schema to dynamically construct request URLs and payloads.