Skip to content

Configuration

Everything is configured through environment variables (all optional):

Variable Default Effect
QUERYAPIGATE_HOME current directory Folder holding queryapigate.db (connections, saved queries, API keys, roles and the audit log).
QUERYAPIGATE_DATABASE_URL unset A postgresql:// URL: keep connections, saved queries, run history, API keys, roles and the audit log in that PostgreSQL database instead of queryapigate.db, so several instances can share them. Needs queryapigate[postgres]; copy an existing store across with queryapigate migrate-to-postgres. See the setup guide.
QUERYAPIGATE_ALLOW_WRITES off Allow INSERT/UPDATE/DDL. Otherwise only single read-only statements are accepted.
QUERYAPIGATE_API_KEY unset The shared admin key, with the owner role. When set (or once a scoped key or a named administrator exists), every request except /health, /docs, /console, /openapi.json and /metrics needs an X-API-Key header. Once named owners exist it is the break-glass key: every use is logged and alerted, and it can be removed.
QUERYAPIGATE_EXPORT_DIRS unset Comma-separated absolute folders a local-folder export destination may write under. Unset: only object-storage destinations (s3://, gs://, r2://) are allowed. Never the server's own home.
QUERYAPIGATE_MASK_KEY unset The key column masking's hash action uses. Unset: one is made once in the store and shared by every instance. Changing it changes every hashed value.
QUERYAPIGATE_REDIS_URL unset A redis:// URL: share rate limits and the response cache across every instance and process (serve, mcp, events). Needs queryapigate[redis]. Without it, each process counts and caches on its own.
QUERYAPIGATE_MAX_PAGE_SIZE 1000 Upper limit for page_size.
QUERYAPIGATE_STREAM_MAX_ROWS unset Row cap for a ?stream=true export. Off (unbounded) by default; a malformed value stops startup.
QUERYAPIGATE_CORS_ORIGINS unset Websites allowed to call the API from a browser: comma-separated origins such as https://app.example.com, or *. Off by default.
QUERYAPIGATE_RATE_LIMIT unset Requests allowed per client address, e.g. 60/minute (also second, hour, day). Off by default; a malformed value stops startup.
QUERYAPIGATE_TRUST_PROXY 0 Number of reverse proxies in front of the app whose X-Forwarded-* headers are trusted. Set it (usually 1) behind nginx, a load balancer or a platform router, or every client looks like the proxy.
QUERYAPIGATE_AUDIT_SYSLOG_URL (unset) Stream every audit entry to a SIEM over syslog: tls://host:6514 or tcp://host:514. With QUERYAPIGATE_AUDIT_SYSLOG_CA, and _CERT/_KEY for mutual TLS.
QUERYAPIGATE_AUDIT_OTLP false Stream every audit entry as OTLP log records to the OTLP logs endpoint.
QUERYAPIGATE_AUDIT_KEY (unset) HMAC key (32+ characters) the audit log's hash chain is signed with, so an edit made with only the store can't be re-signed. Several, comma-separated, while rotating it. Unset: plain SHA-256.
QUERYAPIGATE_HSTS_MAX_AGE (unset) Seconds browsers should use only HTTPS for this host (Strict-Transport-Security), sent only on requests that arrived over HTTPS. Unset: not sent - set it once every client reaches the server over HTTPS.
QUERYAPIGATE_POOL_SIZE 5 Idle connections kept per distinct connection setting. 0 turns pooling off.
QUERYAPIGATE_POOL_IDLE_TIMEOUT 300 Seconds an idle pooled connection is kept before it is closed.
QUERYAPIGATE_QUERY_TIMEOUT 30 Seconds a query may run before it is cancelled (HTTP 504). 0 disables the limit. A request can lower it with ?timeout=, never raise it.
QUERYAPIGATE_HOST / QUERYAPIGATE_PORT 127.0.0.1 / 5000 Bind address for queryapigate serve.
QUERYAPIGATE_DEBUG off Flask debug mode. Never enable on a reachable host.
QUERYAPIGATE_H2_JAR bundled Path to a different H2 JDBC jar.
QUERYAPIGATE_SECRET_KEY unset A Fernet key encrypting connection passwords at rest. Off by default (stored as given); needs queryapigate[encryption]. A malformed value stops startup.
QUERYAPIGATE_JSON_LOGS off Emit one JSON object per log line, tagged with the request ID, instead of plain text.
QUERYAPIGATE_SLOW_QUERY_THRESHOLD 1 Seconds a query may take before it is logged as a warning. 0 disables it.
QUERYAPIGATE_HISTORY_LIMIT 50 Runs kept per saved-query version (and shown in lists).
QUERYAPIGATE_HISTORY_RETENTION_DAYS unset Keep every run for this many days instead of a per-version count - browse it with GET /api/v1/history. Best with QUERYAPIGATE_DATABASE_URL.
QUERYAPIGATE_HISTORY_SAMPLE_RATE 1 Fraction of successful runs recorded; failed runs always are.
QUERYAPIGATE_HISTORY_ADHOC_LIMIT 1000 Ad-hoc SQL runs (/execute_sql, MCP) kept in all, unless a retention period is set.
QUERYAPIGATE_HISTORY_ADHOC_SQL text What an ad-hoc run keeps of its SQL: text, hash (a SHA-256 only) or none - choose hash or none if SQL may embed sensitive literals.
QUERYAPIGATE_HISTORY_FLUSH_INTERVAL 1 Seconds between batched history writes; 0 writes inside each request. See Run history.
QUERYAPIGATE_AUDIT_LOG_LIMIT 500 Administrative-change entries kept in queryapigate.db's audit log; older ones roll off. Always a positive count; a malformed value stops startup.
QUERYAPIGATE_LOAD_EXAMPLES unset yes loads the example APIs (reporting, dashboard, export, partner) at startup - idempotent; a malformed value stops startup. Never removes anything: use queryapigate examples unload.
QUERYAPIGATE_AUDIT_LOG_EXPORT_FILE unset Path to also append every audit entry to, one JSON object per line, never capped - for retention beyond the rolling window above.
QUERYAPIGATE_JWT_JWKS_URL / QUERYAPIGATE_JWT_SECRET unset Accept signed-in users' tokens (Authorization: Bearer) from your identity provider or your own backend, with a role (QUERYAPIGATE_JWT_ROLE / QUERYAPIGATE_JWT_ROLE_CLAIM) for what they may do, and parameters bound to their claims (from_claim). See Signed-in users for the related QUERYAPIGATE_JWT_* settings.
QUERYAPIGATE_EVENTS_MAX_STREAMS 4 Concurrent GET /events streams the main server holds open (each takes a request thread); beyond it, 503. 0 turns it off.
QUERYAPIGATE_EVENTS_PORT 5002 Port of queryapigate events, the separate live-events server for many clients - see Live events.
QUERYAPIGATE_EVENTS_MAX_CONNECTIONS 10000 Open streams one queryapigate events process accepts.
QUERYAPIGATE_EVENTS_POLL_INTERVAL 1 Seconds between queryapigate events checks for new runs (on PostgreSQL a notification usually arrives first).
QUERYAPIGATE_EVENTS_RETENTION_HOURS 24 Hours the event log keeps an event: how far back a queryapigate events client can resume with Last-Event-ID. A whole number, at least 1.
QUERYAPIGATE_MCP_PORT 5001 Bind port for queryapigate mcp - see documentation/MCP.md.
QUERYAPIGATE_MCP_MAX_ROWS 200 Row cap for an MCP tool call's result. Always a positive count; a malformed value stops startup.
QUERYAPIGATE_SSO_ISSUER / QUERYAPIGATE_SSO_CLIENT_ID / QUERYAPIGATE_SSO_CLIENT_SECRET / QUERYAPIGATE_SSO_REDIRECT_URL unset Administrators sign in to the Console through your identity provider, their role from their groups (QUERYAPIGATE_SSO_ROLES). See Console sign-in through SSO.
QUERYAPIGATE_MCP_RESOURCE_URL / QUERYAPIGATE_MCP_AUTH_SERVER unset Let hosted AI clients sign in through your identity provider (OAuth) - the MCP server's own URL and the provider's issuer, with QUERYAPIGATE_MCP_ROLE / QUERYAPIGATE_MCP_ROLE_CLAIM and QUERYAPIGATE_MCP_SCOPE. See Sign-in for AI clients.

The Console's Settings screen shows every one of these with its effective value and whether it was set or is the default (secrets are reported as configured or not, never shown). Settings that hold secrets can also be read from a file - QUERYAPIGATE_SECRET_KEY_FILE and the like: see the server's own secrets, from files.

Calling the API from a browser

Browsers refuse cross-origin JSON calls unless the server allows them. List the sites that may call the API:

QUERYAPIGATE_API_KEY=change-me QUERYAPIGATE_CORS_ORIGINS=https://app.example.com queryapigate serve

Preflight checks are answered automatically, and the pagination headers (X-Has-More etc.) are exposed to the page's JavaScript. CORS only tells the browser which sites may call; it is not authentication, so keep the API key. Avoid * without a key: any website a visitor opens could then reach your databases through their browser (the server logs a warning if you start that way).

Rate limiting

QUERYAPIGATE_RATE_LIMIT=60/minute gives each client address a bucket of 60 requests that refills steadily, so short bursts work but the sustained rate is capped. Over the limit, requests get 429 with a Retry-After header, and every response carries X-RateLimit-Limit and X-RateLimit-Remaining. The limit is applied before the API key check, so guessing keys is throttled too; /health and CORS preflights are never counted. With QUERYAPIGATE_REDIS_URL set, limits are counted in Redis - one budget per client address and per key across every instance and process (serve, mcp, events); if Redis fails, each process falls back to counting on its own until it's back, and an alert says so. Without Redis, state is per process: with several workers or instances, the effective limit is multiplied by their number.

An API key can also carry its own rate_limit (same grammar), checked in addition to the server-wide limit, never instead of it - a request has to pass both. See Per-key rate limiting.