5e-database-sqlite
D&D 5e SRD data as a single SQLite file, built from the JSON published by 5e-bits/5e-database.
Currently tracking upstream release
v5.10.0.
The pin lives in pyproject.toml ([tool.dnd5e-database-sqlite]).
Using the database
Grab dnd5e-<edition>-<language>-<tag>.sqlite3 from the
Releases page and
open it with any SQLite client — no server, no dependencies. (Releases are
published automatically when a vX.Y.Z tag matching the upstream release is
pushed; if none exists yet for the version you need, see
Build the database below to make your own in one
command.) Every collection is a table (monsters, spells, equipment, …)
with three columns:
| column | meaning |
|---|---|
index |
unique id, primary key ("adult-black-dragon") |
name |
display name, indexed |
data |
the complete record as JSON — same shape as the dnd5eapi.co API |
-- simple lookup (uses the name index)
SELECT data FROM spells WHERE name = 'Fireball';
-- query into the JSON
SELECT name, json_extract(data, '$.challenge_rating') AS cr
FROM monsters WHERE cr >= 20 ORDER BY cr DESC;
-- expand a JSON array: every monster immune to poison
SELECT m.name
FROM monsters m, json_each(m.data, '$.damage_immunities') d
WHERE d.value = 'poison';
import sqlite3, json
db = sqlite3.connect("dnd5e-2014-en-v5.10.0.sqlite3")
fireball = json.loads(db.execute(
"SELECT data FROM spells WHERE name = ?", ("Fireball",)
).fetchone()[0])
print(fireball["desc"][0])
JSON functions require SQLite 3.38+ (or any build with the JSON1 extension —
that's virtually every runtime since 2016). The metadata table records which
upstream release, edition, and language the file was built from, plus license
and attribution.
What's inside data
data is the complete record, not a subset — every field the collection's
model defines, nested objects and arrays intact, dropped keys only where the
original was absent (nulls aren't stored). It's a value-exact round trip
of the upstream JSON record: parsing data gives back exactly the keys and
values upstream published (key order and whitespace follow our serializer,
not upstream's files), cross-references included (school, classes,
damage.damage_type, etc. are all {index, name, url} pointers you resolve
with a second query — there are no SQL foreign keys).
The shape is per-collection, not uniform: a spells row's data follows the
spell schema, a races row's follows the race schema. There's no single
"record" shape across tables. The canonical definition of each collection's
shape lives in this repo's pydantic models (one file per collection —
dnd5e_db/models/<edition>/<collection>.py), which mirror upstream's zod
schemas 1:1; each file's docstring names the exact upstream .ts schema it
mirrors, under 5e-bits/5e-database's src/*/schemas/
— useful as a language-neutral reference if you're not working in Python.
Installing as a library
pip install 5e-database-sqlite
(Installing straight from git works too:
pip install git+https://github.com/vkama/5e-database-sqlite.git.)
SRD.open() and SRD2024.open() both work right away with no local checkout
needed — 2014/en and 2024/en databases both ship bundled inside the package.
Python API
From Python you don't need SQL at all — every record is a typed pydantic model with autocomplete:
from dnd5e_db.srd import SRD
with SRD.open() as srd: # 2014/en: builds from data/ in a checkout,
# or opens the db bundled in the package otherwise
aboleth = srd.monsters["aboleth"] # by index
aboleth.challenge_rating # 10.0
aboleth.speed.swim # "40 ft."
srd.spells.named("Fireball") # by display name
[m.name for m in srd.monsters if m.challenge_rating >= 20]
[s.name for s in srd.spells if s.level == 3 and s.school.index == "evocation"]
Collections support [], .get(), in, len(), iteration, and .named() —
plain Python, no query language.
Discovering a collection's fields
srd.spells and srd.races don't hand you dicts — each item is a specific
model instance (Spell, Race), so what properties it has is discoverable
the same way as any typed Python object:
- Autocomplete.
srd.spells["fireball"].in an editor/type checker listsdesc,range,level,damage,school,classes, … —Collection[M]is generic, sosrd.spellstypes asCollection[Spell]andsrd.racesasCollection[Race]without running anything. - At runtime,
Spell.model_fields(orRace.model_fields) gives you{field_name: FieldInfo(type, ...)}for every field, nested model types included;Spell.model_json_schema()expands that recursively into a full JSON Schema document. - In source,
dnd5e_db/models/srd2014/spells.py/.../races.pyare the canonical definitions — see "What's insidedata" above.
The 2024 SRD preview reads the same way through SRD2024.open() — identical
interface, 2024 collections (species/subspecies instead of
races/subraces, plus poisons and weapon_mastery_properties; no spells
or rules — upstream hasn't ported them yet, and monsters currently holds only
a handful of records).
License
The D&D SRD content is © Wizards of the Coast LLC, released under
CC-BY-4.0; each database file
embeds the required attribution in its metadata table. The upstream JSON is
maintained by 5e-bits/5e-database
(MIT). The build tooling in this repo is released under MIT.
Setup (building it yourself)
Requires Python 3.14. If it's not already on your system, install it locally with uv (no sudo needed):
uv python install 3.14
poetry env use $(uv python find 3.14)
Then:
poetry install
Build the database
The upstream JSON is already vendored under data/ (checked into git), so a
plain build works right away — no fetch step needed:
poetry run python -m dnd5e_db.build
# -> dist/dnd5e-2014-en-v5.10.0.sqlite3
poetry run python -m dnd5e_db.build --edition 2024 # dist/dnd5e-2024-en-v5.10.0.sqlite3
build validates the vendored data against our pydantic models first and
refuses to write if anything doesn't match — a validation failure means
upstream changed shape, not that it's safe to skip.
Bumping to a newer upstream release also involves dnd5e_db.fetch
(re-download the vendored data for a new tag), dnd5e_db.validate (drift
report without building), and dnd5e_db.check (is the pin still upstream's
latest?) — see CLAUDE.md for the full workflow.
Releasing
Pushing a vX.Y.Z tag matching the pinned upstream release (or a -rN
suffix for a tooling-only re-release of the same data) runs
.github/workflows/release.yml, which:
- Runs the stable gate (tests +
validate --edition 2014). - Builds the 2014/en database and attaches it to a new GitHub Release.
- Builds the sdist/wheel with the package version set to the tag
(PEP 440-normalized:
v5.10.0→5.10.0,v5.10.0-r2→5.10.0.post2) and publishes to PyPI (bundling both editions' databases — see step 4). - A second, independent job runs the 2024 preview's own gate (
pytest -m srd2024+validate --edition 2024), builds the 2024/en database, and attaches it to the same GitHub Release as a second asset. This job only runs after step 1-3 succeed (it needs the release to already exist to upload to), but never blocks them — if the 2024 preview corpus has drifted, only this job fails; the 2014 release and PyPI publish already went out.
workflow_dispatch dry-runs the two database jobs — same gates and builds,
uploaded as workflow artifacts, nothing published. The PyPI job is tag-gated
and simply doesn't run on a manual dispatch, so the sdist/wheel build has no
dry-run mode.
PyPI publishing uses Trusted Publishing
(OIDC) rather than a stored API token — nothing to generate or rotate. It
needed a one-time setup on PyPI before the first release (already done for
this repo; kept here for forks or a project rename): on the
"Publishing" tab of a PyPI
account with rights to the 5e-database-sqlite project name (or via "pending
publisher" if the project doesn't exist on PyPI yet), register a GitHub
publisher with:
| field | value |
|---|---|
| PyPI project name | 5e-database-sqlite |
| Owner | vkama |
| Repository name | 5e-database-sqlite |
| Workflow name | release.yml |
| Environment name | (leave blank) |
Vendoring this repo in your own build
If your project wants the built .sqlite3 as part of its build (rather than
just grabbing a released file), pull this repo in as a git submodule and build
from inside it:
git submodule add https://github.com/vkama/5e-database-sqlite.git vendor/5e-database-sqlite
cd vendor/5e-database-sqlite
poetry install
poetry run python -m dnd5e_db.build --out ../../assets # --out sends the db wherever you want it
--out is an existing flag on dnd5e_db.build — nothing above needs Poetry
specifically either: the only runtime dependency is pydantic, so pip install pydantic plus python -m dnd5e_db.build --out <path> (run from the submodule
root) works in any Python 3.14 toolchain that doesn't want Poetry involved.
A submodule pins an exact commit, so picking up a newer upstream release means
git submodule update --remote (or checking out a new tag) plus a commit in
your repo — bumps stay explicit and reviewable. Note that vendoring pulls in
the full data/ tree (~7.5 MB) as part of the clone. If you just want the file
without rebuilding anything, see "Using the database" above.
Test
poetry run pytest
Metadata
Release files for 5e-database-sqlite 5.10.0.post5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| 5e_database_sqlite-5.10.0.post5.tar.gz | 880.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 5e_database_sqlite-5.10.0.post5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.8 MB
Release files / 5e_database_sqlite-5.10.0.post5.tar.gz
| Download URL | 5e_database_sqlite-5.10.0.post5.tar.gz |
|---|---|
| Size | 880.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6bcab3dd705c16ee36c8cbf6ff88b24755c4ef2c39c3d8e8f076e70c22ab612b
|
|
BLAKE2b-256 checksum How to use checksums |
af41ecc4b2e828ea8280b2ecb8493254e057b268b38f8ff822dd42d1f9c05a7b
|
| 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 Aug 19, 2026.
Transparency logRelease files / 5e_database_sqlite-5.10.0.post5-py3-none-any.whl
| Download URL | 5e_database_sqlite-5.10.0.post5-py3-none-any.whl |
|---|---|
| Size | 904.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
298e18873d126d0547012af93087e1b360c6113377ae44b35450688eb0819481
|
|
BLAKE2b-256 checksum How to use checksums |
a3bafd680874802b2fb2cc47d9b516a753284625543737d854fdba382282874e
|
| 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 Aug 19, 2026.
Transparency log