Skip to main content

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 byte-for-byte round trip of the upstream JSON record: re-serializing it reproduces exactly what upstream published, 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

(Not published yet as of this writing — see "Releasing" below. Until the first tagged release goes out, install straight from git instead: 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 lists desc, range, level, damage, school, classes, … — Collection[M] is generic, so srd.spells types as Collection[Spell] and srd.races as Collection[Race] without running anything.
  • At runtime, Spell.model_fields (or Race.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.py are the canonical definitions — see "What's inside data" 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:

  1. Runs the stable gate (tests + validate --edition 2014).
  2. Builds the 2014/en database and attaches it to a new GitHub Release.
  3. 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).
  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 runs the same gates and builds as a dry run (uploads workflow artifacts) without publishing anything.

PyPI publishing uses Trusted Publishing (OIDC) rather than a stored API token — nothing to generate or rotate, but it needs a one-time setup on PyPI before the first release: 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 once releases are published.

Test

poetry run pytest

Metadata

Release files for 5e-database-sqlite 5.10.0.post4

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

Source distribution (sdist)

Source distribution for 5e-database-sqlite 5.10.0.post4
File Size Uploaded
5e_database_sqlite-5.10.0.post4.tar.gz 879.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for 5e-database-sqlite 5.10.0.post4
File Interpreter ABI Platform
5e_database_sqlite-5.10.0.post4-py3-none-any.whl Python 3 none any Details

Total release size: 1.8 MB

Release files / 5e_database_sqlite-5.10.0.post4.tar.gz

Download URL 5e_database_sqlite-5.10.0.post4.tar.gz
Size 879.3 kB
Tags Source
SHA-256 checksum
How to use checksums
e48d56dd8005c0fd9840a42a01c6e9c70bfe6647eaf09896eb9ed87a17fec2c9
BLAKE2b-256 checksum
How to use checksums
8af7a33665e0ef1eab4367cc37becf7654d2ac9f656412fc2a111eab3e3bce24
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 7, 2026.

Transparency log

Release files / 5e_database_sqlite-5.10.0.post4-py3-none-any.whl

Download URL 5e_database_sqlite-5.10.0.post4-py3-none-any.whl
Size 903.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0efc2bf6ecddd7f956f8fa9ebc80181ba26a3c9f0b8f98ed108988a3327c9296
BLAKE2b-256 checksum
How to use checksums
fff4c900f1b5dd01510e2fda9121eadbd96d8eea982b2b6afd50634715f6b8cb
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 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

5.10.0.post4 This release

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