Skip to main content

SQL2API

CI License: MIT Python

Turn SQL into a REST API. SQL2API is a small Flask service that runs SQL against your databases and returns the results as JSON, NDJSON, CSV, TSV, XML, YAML or Excel. Save a query once and it becomes an endpoint with typed, injection-safe parameters, versioning and run history.

$ curl 'http://127.0.0.1:5000/q/actor_by_id?id=7'
[{"actor_id": 7, "first_name": "GRACE", "last_name": "MOSTEL"}]

$ curl 'http://127.0.0.1:5000/q/films_by_rating?rating=PG&max_length=60&format=csv&page_size=2'
film_id,title,rating,length
410,HEAVEN FREEDOM,PG,48
443,HURRICANE AFFAIR,PG,49
Database JSON NDJSON XML YAML CSV TSV XLSX
MySQL ✅ ✅ ✅ ✅ ✅ ✅ ✅
PostgreSQL ✅ ✅ ✅ ✅ ✅ ✅ ✅
ClickHouse ✅ ✅ ✅ ✅ ✅ ✅ ✅
SQLite ✅ ✅ ✅ ✅ ✅ ✅ ✅
H2 ✅ ✅ ✅ ✅ ✅ ✅ ✅

Features

  • Ad-hoc queries - POST /execute_sql with SQL and a connection name.
  • Saved, versioned queries - every save creates a new version; GET /q/<name>?id=7 runs the latest one (or ?version=1). Each run is recorded in the query's execution history.
  • Bound parameters - write WHERE id = :id and the value is sent to the database separately from the SQL, so it cannot inject anything. Declare types ({"id": "int"}) and query-string values are converted for you.
  • Pagination - ?page=2&page_size=50, with X-Has-More telling you whether another page exists.
  • Connection pooling - MySQL, PostgreSQL, ClickHouse and H2 connections are reused between requests instead of opened for each one (about 30x lower per-request overhead on MySQL and H2 against a local server; more over a network).
  • Query time limit - runaway queries are cancelled on the database (30 s by default, ?timeout= per request) so they cannot tie up the service.
  • Read-only by default - only single SELECT/WITH/SHOW/DESCRIBE/EXPLAIN statements run, and sessions are opened read-only where the database supports it.
  • Secrets stay out of files - "password": "${PG_PASSWORD}" in db_connections.json reads the environment.
  • Self-documenting - OpenAPI at /openapi.json, Swagger UI at /docs.

Install

pip install "sql2api[postgres]"         # pick the drivers you need: mysql, postgres, clickhouse, h2
# or everything:                        pip install "sql2api[all]"

SQLite needs no extra driver. H2 also needs a Java runtime (the H2 JDBC jar is bundled). From a clone: pip install -e ".[dev]". Or use Docker - see below.

Quick start

The repository ships two sample SQLite databases and a couple of saved queries:

cd examples
cp db_connections.example.json db_connections.json
sql2api serve                            # http://127.0.0.1:5000
curl -X POST 'http://127.0.0.1:5000/execute_sql?page_size=3' -H 'Content-Type: application/json' \
     -d '{"sql": "SELECT * FROM actor WHERE actor_id > :min", "params": {"min": 10}, "connection_name": "sakila-sqlite"}'

Open http://127.0.0.1:5000/docs for the interactive API reference.

For your own databases, run sql2api init in an empty folder: it creates db_connections.json (inactive templates for every supported database) and saved_sql/. Edit the file, set "active": true, and start the server there.

Saving a query as an endpoint

curl -X PATCH http://127.0.0.1:5000/save_sql_to_file -H 'Content-Type: application/json' -d '{
  "filename": "actor_by_id",
  "sql_query": "SELECT * FROM actor WHERE actor_id = :id",
  "query_parameters": {"id": "int"},
  "connection_name": "sakila-sqlite",
  "author": "me", "description": "Look up an actor"
}'

curl 'http://127.0.0.1:5000/q/actor_by_id?id=7&format=yaml'

Saving again under the same name adds version 2; DELETE /saved_sql/actor_by_id?version=1 removes one version.

Configuration

Everything is configured through environment variables (all optional):

Variable Default Effect
SQL2API_HOME current directory Folder holding db_connections.json and saved_sql/.
SQL2API_ALLOW_WRITES off Allow INSERT/UPDATE/DDL. Otherwise only single read-only statements are accepted.
SQL2API_API_KEY unset When set, every request (except /health and /docs) needs a matching X-API-Key header.
SQL2API_MAX_PAGE_SIZE 1000 Upper limit for page_size.
SQL2API_POOL_SIZE 5 Idle connections kept per distinct connection setting. 0 turns pooling off.
SQL2API_POOL_IDLE_TIMEOUT 300 Seconds an idle pooled connection is kept before it is closed.
SQL2API_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.
SQL2API_HOST / SQL2API_PORT 127.0.0.1 / 5000 Bind address for sql2api serve.
SQL2API_DEBUG off Flask debug mode. Never enable on a reachable host.
SQL2API_H2_JAR bundled Path to a different H2 JDBC jar.

Security

SQL2API runs whatever SQL it is given against your databases, so it ships locked down and expects you to finish the job:

  • Set SQL2API_API_KEY and serve over TLS (put it behind a reverse proxy).
  • Connect with a database account that only has the privileges the API needs - the read-only guard is defence in depth, not a replacement for grants. (H2's driver cannot enforce read-only, so H2 relies on the guard.)
  • Use bound :name parameters. The older {name} placeholders paste text into the SQL and are therefore restricted to numbers and plain text.
  • Saved-query files are only read from saved_sql/; passwords are never returned by the API.

See SECURITY.md to report a vulnerability.

Docker

docker build -t sql2api .                          # add --build-arg WITH_H2=true for H2 support
docker run -p 5000:5000 -v "$PWD/data:/data" -e SQL2API_API_KEY=change-me sql2api

The container keeps db_connections.json and saved_sql/ in /data. It runs gunicorn with a single worker (the files are protected by an in-process lock).

API overview

Endpoint Method Purpose
/execute_sql POST Run ad-hoc SQL (sql, connection_name, optional params).
/q/<name> GET, POST Run a saved query; query-string or body values become parameters.
/save_sql_to_file PATCH Save a query (creates the next version).
/list_files GET List saved queries and their versions (sort_by, sort_order).
/saved_sql/<name> DELETE Delete a saved query or one ?version=.
/view_file_content GET Raw content of a saved query file.
/execute_sql_from_file, /execute_sql_with_parameters_from_file POST Run a saved query by filepath (same as /q/<name>).
/connections GET, PATCH List (passwords masked) / add / update connections.
/connections/<name> DELETE Remove a connection.
/health, /docs, /openapi.json GET Liveness, Swagger UI, OpenAPI spec.

Full details are in documentation/API.md.

Development

pip install -e ".[dev]"
ruff check .
python -m unittest discover -s tests -t .

The integration tests in tests/test_integration.py run against real MySQL, PostgreSQL, ClickHouse and H2 servers when the matching SQL2API_IT_* variables are set, and are skipped otherwise; CI runs them against service containers. See CONTRIBUTING.md for the pull request process, and CHANGELOG.md for what changed.

Third-party components

The wheel bundles the H2 Database JDBC driver (MPL 2.0 / EPL 1.0). The sample SQLite databases in examples/ derive from the Sakila and Chinook sample datasets.

License

MIT © Anantha Raju C

Contact

Anantha Raju C - @anantharajuc - arcswdev@gmail.com

Project link: https://github.com/AnanthaRajuC/SQL2API

Release files for sql2api 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sql2api 0.2.0
File Size Uploaded
sql2api-0.2.0.tar.gz 2.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for sql2api 0.2.0
File Interpreter ABI Platform
sql2api-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 5.1 MB

Release files / sql2api-0.2.0.tar.gz

Download URL sql2api-0.2.0.tar.gz
Size 2.5 MB
Tags Source
SHA-256 checksum
How to use checksums
aadc7ef4835fdabe19d3711662b5459e2c1fed53c969c0fba9bc656018f9276e
BLAKE2b-256 checksum
How to use checksums
b13c78f884ad4f9e71f3042bc7803b1342b9d769e24c7cadc29d4afeb20631d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / sql2api-0.2.0-py3-none-any.whl

Download URL sql2api-0.2.0-py3-none-any.whl
Size 2.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
2dd8ab5f929d65b80aa02d836248b2eeb2fa27d246326da4d329d875bba16ca6
BLAKE2b-256 checksum
How to use checksums
ab74c668bd85ba34fc0163f5b3c0fa782830fc84f6bfe09dadfa59f4e945aed4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page