SQL2API
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_sqlwith SQL and a connection name. - Saved, versioned queries - every save creates a new version;
GET /q/<name>?id=7runs the latest one (or?version=1). Each run is recorded in the query's execution history. - Bound parameters - write
WHERE id = :idand 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, withX-Has-Moretelling you whether another page exists. - Read-only by default - only single
SELECT/WITH/SHOW/DESCRIBE/EXPLAINstatements run, and sessions are opened read-only where the database supports it. - Secrets stay out of files -
"password": "${PG_PASSWORD}"indb_connections.jsonreads 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_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_KEYand 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
:nameparameters. 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.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sql2api-0.1.0.tar.gz | 2.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sql2api-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 5.0 MB
Release files / sql2api-0.1.0.tar.gz
| Download URL | sql2api-0.1.0.tar.gz |
|---|---|
| Size | 2.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8197bc44e87ec9dcfef9cc427f6a9ddc80e181d835ccac954828bea9cd0a4b44
|
|
BLAKE2b-256 checksum How to use checksums |
7cb963092c191ebb7c8ac7d095c099cf6879b1bb8b6646789c9a0a811cbf97cb
|
| 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 logRelease files / sql2api-0.1.0-py3-none-any.whl
| Download URL | sql2api-0.1.0-py3-none-any.whl |
|---|---|
| Size | 2.5 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5778ac0df3fc661a48862ca77fe7ba91a6b8e9e72cfbb1767d5bb1907c8f00f4
|
|
BLAKE2b-256 checksum How to use checksums |
36638f6ee0a20dd3ff2509d0d56067c21cda56d7c65b067ad354f20503d97813
|
| 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