> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# Quickstart: SQLite/Python

Last updated 29 October 2026

From sign-in to a first replicated commit with Python’s [`sqlite3`](https://docs.python.org/3/library/sqlite3.html), from your laptop or any machine with Python 3.11 or later and internet access, with nothing to allowlist, on Linux (x64 or arm64) or macOS on Apple silicon. The Python must be one that can load SQLite extensions; the install step below sets one up with uv (see [Requirements](https://tonbo.dev/docs/databases/python.md#requirements)).

## Sign in

Sign in at [tonbo.dev](https://tonbo.dev/login) with GitHub or Google. A new account asks you to choose a username: it names your personal account, which holds your databases, streams and keys, and appears in the console’s addresses. You can change it once every 90 days.

## Create a database

Create a database named `notes`. It goes to the default region, `us-east-1`, unless you choose another (see [Regions and URLs](https://tonbo.dev/docs/databases/manage.md#regions)): pick the one nearest where your program runs. With the CLI, name it with `--region`, as in `tonbo database create notes --region ap-southeast-1`.

**Console**

1. Open [Databases](https://tonbo.dev/) in the console.
2. Choose **Create database** and name it `notes`.

**CLI**

```shell
npm i -g @tonbo/cli
tonbo login
tonbo database create notes
```

`tonbo login` signs you in through a browser on the same machine. On a machine without one, such as a remote server, set `TONBO_API_KEY` to a key that acts as you and is not narrowed, created in the console under **API keys**, and the CLI uses it instead.

**API**

With a key that acts as you and is not narrowed, in `TONBO_API_KEY`: create one in the console under **API keys** without narrowing it. The next step’s key is narrowed, and a narrowed key never creates databases.

```shell
curl -sS https://api.tonbo.dev/v1/databases \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"notes"}'
```

It answers `201` once the database is active, or `202` while it is still being created.

The database is creating for a moment, then active. Node.js code opens it by name, and Python code by its URL, which `tonbo database show notes` and the console show (see [Regions and URLs](https://tonbo.dev/docs/databases/manage.md#regions)).

## Create an API key

Give the program its own key, narrowed to `notes`: it reads and writes only that database, and may name one that does not exist yet. A key that acts as you and is not narrowed reaches every database and stream you can in its account (see [What a key can reach](https://tonbo.dev/docs/platform/api-keys.md#key-scope)).

**Console**

1. In the [console](https://tonbo.dev/), choose **API keys** in the sidebar.
2. Choose **New API key** and narrow it to the database `notes`.
3. Copy the key. It starts with `tbo_` and is shown once.

**CLI**

```shell
tonbo api-key create notes-app --database notes
```

It prints the key once, as `TONBO_API_KEY=...`, ready for the program’s environment. Put it before the command that runs the program, as the next step does, rather than exporting it in your shell: the CLI reads `TONBO_API_KEY` too, and would then act with the narrowed key.

On this machine, after `tonbo login`, a Node.js program can also open its databases with no key at all: `open()` falls back to your login (see [Connect](https://tonbo.dev/docs/databases/connect.md)). Python does not, and the narrowed key is what any program uses anywhere else.

## Install the package

```shell
uv venv --managed-python --python 3.13
source .venv/bin/activate
uv pip install sqlite-ursula==0.11.3.dev0
python -m sqlite_ursula
```

This uses [uv](https://docs.astral.sh/uv/getting-started/installation/): install it first if you have not. It makes a virtual environment on a Python that uv manages, installs the package into it, and checks that this Python can run the package: the last line prints `result: ok`. Python uses the pre-release sqlite-ursula 0.11.3.dev0 until 0.11.3 is published to PyPI.

The package runs inside Python’s own `sqlite3`, so not every Python can run it: the python.org macOS installer, Apple’s `/usr/bin/python3` and Debian’s or Ubuntu’s system Python cannot (see [Requirements](https://tonbo.dev/docs/databases/python.md#requirements)). In a Python that can, install it with pip as `pip install sqlite-ursula==0.11.3.dev0`, or add it to a uv project with `uv add sqlite-ursula==0.11.3.dev0`: keep the exact version, which is what lets both install a pre-release.

## Open and commit

Python opens a database by its URL. Put the URL of `notes` in `NOTES_URL` (the console shows it too, on the database’s page):

```shell
export NOTES_URL=$(tonbo database show notes --json | jq -r .url)
```

Save this as `app.py`. It sends the API key itself as its token:

**app.py**

```python
import os

import sqlite_ursula

# Opens notes by its URL with your API key; db is a sqlite3.Connection.
db = sqlite_ursula.open(
    os.environ["NOTES_URL"],
    token=os.environ["TONBO_API_KEY"],
    dir="./notes-cache",
)
db.execute("CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, body TEXT)")
db.execute("INSERT INTO notes(body) VALUES (?)", ("hello",))
db.commit()  # committed on this machine
db.synced()  # Tonbo now holds every commit made so far
print(db.execute("SELECT count(*) FROM notes").fetchone()[0], db.status()["commits"])
db.close()
```

Run it with the key from the API key step, in the shell where you activated the environment and set `NOTES_URL`:

```shell
TONBO_API_KEY=tbo_... python app.py
```

It prints the count, then the commit counters with nothing pending: `logged`, `confirmed` and `acknowledged` are one number, the position of the last commit:

```text
1 {'logged': <n>, 'confirmed': <n>, 'acknowledged': <n>, 'pending': 0, 'pending_bytes': 0}
```

Call `synced()` before anything outside the process acts on a commit, such as a response: until then the commit is only on this machine. Python does not fall back to your `tonbo login`: pass the key as `token`, as here.

## Rebuild it from Tonbo

Delete the local copy and run the program again. The open rebuilds the database from the copy Tonbo keeps, so the first row is still there and the count becomes 2:

```shell
rm -r notes-cache
TONBO_API_KEY=tbo_... python app.py
```

Then copy `app.py` to another machine, install the package and run it with the same key and the same `NOTES_URL`. It opens the same database, and the count becomes 3.

## Add it to a project

In a project of your own, with the program’s API key in `TONBO_API_KEY` where the project keeps its secrets, never in its code:

1. Create the database with `tonbo database create notes`, signed in with `tonbo login`, or in the console. `tonbo database show notes` prints `State:` `active` once it is ready.
2. Install with `pip install sqlite-ursula==0.11.3.dev0`, or `uv add sqlite-ursula==0.11.3.dev0` in a uv project, in a Python that can load SQLite extensions (see [Requirements](https://tonbo.dev/docs/databases/python.md#requirements)). `python -m sqlite_ursula` then prints `result: ok`.
3. Python opens a database by its URL, which `tonbo database show notes --json | jq -r .url` prints: put it in `NOTES_URL` beside the key. Where the program opens SQLite, open it instead with `sqlite_ursula.open(os.environ["NOTES_URL"], token=os.environ["TONBO_API_KEY"], dir="./notes-cache")`. It is a `sqlite3.Connection`, so the SQL stays as it is.
4. Call `db.synced()` after a commit, before anything outside the process acts on it: until then the commit is only on this machine.

One process writes a database at a time: the last one to open it (see [When two instances run](https://tonbo.dev/docs/databases/how-it-works.md#takeover)).

## Next steps

- [Connect from Python](https://tonbo.dev/docs/databases/python.md): The Pythons that run the package, keys that act as an Agent, `gate()`, asyncio, frameworks and LangGraph.
- [Rules for databases](https://tonbo.dev/docs/databases/rules.md): What to do and what not to, before a program relies on a database.
- [Guarantees](https://tonbo.dev/docs/databases/how-it-works.md): Why one process writes a database at a time, and what happens to the first when a second opens it.
- [Backups](https://tonbo.dev/docs/databases/backups.md): Export a plain SQLite copy to take a database elsewhere or go back after a mistake; the cache directory is not one.
- [Limits](https://tonbo.dev/docs/databases/limits.md): Transaction, snapshot and database size, requests per second, and databases per account.
- [Troubleshooting](https://tonbo.dev/docs/databases/troubleshooting.md): What each error and status means, and what to do about it.
