Library / SDK
rogerbinns/apsw avatar
rogerbinns/apsw

APSW and the case for driving SQLite through its own C API

Another Python SQLite wrapper

950 stars110 forksCNOASSERTION

At a glance

What is it?
APSW glues the complete SQLite C API and Python's C API together and stays current with both. It is a C extension with its own release cycle, a custom licence file, and a documented argument against replacing the standard sqlite3 module in most projects.
Who is it for?
APSW fits Python programs that need SQLite features the standard library driver does not expose, such as the session extension, virtual tables, a custom VFS, the JSON functions or direct access to CArray pointers, and it fits projects already committed to a C extension build.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The full SQLite C API, not the subset Python happens to expose

APSW stands for Another Python SQLite Wrapper, and the description is deliberately literal. It lets you get the most out of the embedded SQLite database engine from Python, and the most out of Python from SQLite. The mechanism is stated plainly: APSW glues together the complete SQLite C API and Python's C API, staying up to date with both SQLite and Python.

That word, complete, is the whole design. The standard library's sqlite3 module exposes a curated subset of what SQLite can do, and it exposes it in a way that is convenient but lossy. APSW is built for the person who needs what the curated layer leaves out, and who is willing to work at the C level to get it. It supports CPython 3.11 onwards, so this is a current tool rather than a legacy one, and the primary language in the repository is C, with the Python package wrapping it.

The list of areas covered is broad, and each one maps to a SQLite feature rather than to a convenience. Full text search, the session extension, virtual tables, the virtual file system, JSON functions, and CArray, which exposes raw memory to Python. All of that is available in ordinary synchronous code, and there is also full support for the async frameworks.

The intended users are Python developers who know SQLite well and want its full surface, and people who came to SQLite from another engine and want the features the standard driver leaves behind. The homepage is rogerbinns.github.io/apsw, the repository is not archived, and the last push was on 2026-10-19.

The project tells you when not to use it

Most wrapper projects argue that you should use them. APSW does the opposite, in a sentence that is easy to skim past.

The documentation states that it is recommended to use the builtin sqlite3 module if you want SQLite to appear interchangeable with the other database drivers. Use APSW when you want to use SQLite fully, and have an improved developer experience. The documentation has a section on the differences between APSW and sqlite3, and a tour with example code at rogerbinns.github.io/apsw/example.html.

That recommendation is a real constraint on adoption, and it is worth taking at face value rather than looking for a reason to ignore it. The builtin module aims at substitutability: a codebase can swap the driver without rewriting the calls. APSW aims at capability: it is a different shape of API, with the C API showing through, and code written against it does not port to another driver without work.

So the decision is not which wrapper is better. It is whether your project's constraints are about capability or about interchangeability. A reporting script that must run anywhere with any database driver should use the builtin module. A service that needs a virtual table or a session or a VFS has no equivalent in the builtin module at all, and there APSW is not merely nicer, it is the only one of the two that can express the requirement.

The author also keeps the project reachable directly, through a Python SQLite discussion group on Google Groups, GitHub discussions, and email to the author.

Version numbers track the SQLite release, not the wrapper's own

The APSW version scheme is the first thing that confuses people who expect semantic versioning. The recent releases are 3.54.1.0 on 2026-10-19, 3.54.0.0 on 2026-10-12 and 3.53.4.0 on 2026-07-26, and the first three components are the SQLite version being bundled.

The Makefile makes the scheme explicit. The SQLite version, the APSW suffix and the release date are separate variables.

make
SQLITEVERSION=3.54.1
APSWSUFFIX=.0
RELEASEDATE="18 October 2026"

The final component, the APSW suffix, is the wrapper's own revision, and it is what tells you whether a SQLite upgrade came with wrapper changes. A release where the suffix changes is one where something in the glue layer moved.

This scheme has a practical consequence for upgrades that a normal version number does not give you. When you upgrade APSW, you are also upgrading your SQLite, because they ship together. If you depend on a specific SQLite behaviour, or on a bug fix, or on a behaviour change in a particular SQLite release, that dependency is expressed in the APSW version you pin, and the two cannot be separated without building from source. The Makefile has a checkversion target, which suggests the project treats a mismatch between the declared SQLite version and the actual one as something to catch automatically.

The generated files list in the Makefile shows how much of the wrapper is produced rather than written: apswversion.h, the docstrings, constants.c, stringconstants.c, a sqlite_extra.json file and the generated documentation. The version is baked into the build, not looked up at runtime.

What you get that the builtin driver does not expose

The feature list is only interesting if you know what each item buys, so it is worth going through the extensions one at a time rather than treating them as a bundle.

Full text search is SQLite's FTS5, with its own ranking and tokenizer behaviour, and the project documents it separately, including a worked example in the examples directory as a separate fts.py file.

The session extension is the one with the clearest use case. It records changes to a set of tables as a changeset, which can then be applied to another database or held and applied later. That is how you replicate a change from one database to another, or how you implement an undo log, without diffing tables by hand. The project ships a session.py example and a session page in the documentation.

Virtual tables let a table be backed by something other than rows in a file, which is how SQLite reaches formats it does not natively understand, and the documentation covers it with its own vtable page.

The virtual file system is the other structural one. A VFS lets you replace how SQLite reads and writes files, which is how you can put a database somewhere unusual, or intercept writes.

JSON support exposes the SQLite JSON functions, and there is a json.py example alongside a jsonb page in the documentation. CArray is the one that is hardest to categorise: it gives Python code a pointer to memory, with the documentation describing it as part of the main apsw page.

Async support is the feature that fits modern Python code best. The documentation states that all functionality is available in regular synchronous code as well as with full support for the async frameworks, and the examples directory contains an async.py.

Installing from PyPI, and the apsw console script

Releases are made to PyPI and to GitHub releases, and the README says to install using pip. The package name on PyPI is apsw, installed with pip, and the documentation points to the PyPI project page for the exact invocation. Wheels are produced for both 64-bit and 32-bit architectures, since the cibuildwheel configuration in pyproject.toml lists the archs as auto64 and auto32.

There is also a command line entry point, declared in pyproject.toml as a console script.

toml
[project.scripts]
apsw = "apsw.__main__:main"

So an install puts an apsw command on your path. The project also publishes an RSS feed of releases from PyPI and announces new releases on the Python SQLite discussion group, which is a practical way to keep track of a project whose version number changes when SQLite changes.

One caveat about the build deserves attention, because it is the reason installation is not always instant. APSW is a C extension, not a pure-Python package, so it needs a compiler when no matching wheel is available for your platform. The extension source lives in the src directory, and the setup.py file includes that directory in the compiler's include path.

python
include_dirs = ["src"]

For a Linux or macOS developer that is a normal requirement. For a locked-down build environment, or for a platform without a published wheel, the source build becomes your problem, and the pyproject build-system section shows that setuptools is the backend used to do it.

A Makefile that regenerates the wrapper and checks the SQLite version

The build system is unusual enough to be worth a look, because it explains how the wrapper stays in step with SQLite.

The Makefile lists the files that are generated rather than hand-written, and they cover most of the surface area of the library. Alongside the version header and the docstrings, there are constants.c and stringconstants.c, which is where the SQLite constants are materialised for Python, and a sqlite_extra.json file that feeds an include file used to build the documentation. The documentation itself is partly generated too, with per-feature files for blob, vfs, vtable, connection, cursor, apsw, session, jsonb, backup and fts, and example documents for fts, session, json and async.

The all target regenerates those files and then runs checkversion, so a mismatch between the declared SQLite version and the compiled library is a build failure rather than a runtime surprise. The Makefile also carries a large set of development targets, including test and fulltest, coverage targets, a stubtest target for checking type stubs against the runtime, a langserver target, compile-win targets for the Windows build, setup-wheel, and a source target.

Two details hint at the care taken over portability. There is a checksums file at the top level of the repository, and there is a SECURITY.md. The stubtest target is the interesting one for anyone using type checkers: it means the shipped .pyi stubs are validated against the actual module, so a mismatch between the documented API and the implemented API is caught in CI rather than in an editor.

A custom licence file, and the security contact

The licence is the least standard part of the project and deserves a careful read before adoption. The licence field on the repository says Other, because the LICENSE file is a custom document that GitHub cannot classify, and the README describes it, in essence, as any OSI approved open source licence. The wording gives you the choice, which means the actual text of the file is what governs, and it is worth reading in full rather than relying on the summary.

For a library that links a Python extension against SQLite and is embedded in other people's applications, the practical questions are attribution, redistribution and patent terms, and the general shape of the answer is an OSI approved licence. That is a lower obstacle than copyleft, but it is still a file you should read, and it is a file that could change between versions.

The repository also ships a SECURITY.md, and the README directs bug reports to the GitHub issues tracker, noting that a new issue can be used to report previously unknown problems. For a project that exposes a C API to Python code, a security policy is not decoration, because a mistake in the glue layer is a memory-safety question rather than a logic question.

The releases themselves are published to both PyPI and GitHub releases, with a full detailed list of changes kept in the project's documentation under changes.html, which is the page to read before an upgrade rather than after one.

Editorial conclusion

APSW fits Python programs that need SQLite features the standard library driver does not expose, such as the session extension, virtual tables, a custom VFS, the JSON functions or direct access to CArray pointers, and it fits projects already committed to a C extension build. It does not fit teams whose main constraint is portability, because APSW is a compiled C extension with no pure-Python fallback, and it does not fit code that must look like every other database driver, since the project itself recommends the builtin sqlite3 module in that case. Verify first that your target Python is 3.11 or newer, that the SQLite version you depend on is present in the APSW release you pin, and that the licence file in the repository matches what your organisation requires for a dependency.

Frequently asked questions

How do I install APSW?

Install it with pip, from the apsw package on PyPI, or download a release from the GitHub releases page. Wheels are built for 64-bit and 32-bit architectures; without a matching wheel, the C extension is compiled from the source in the src directory.

What Python versions does APSW support?

APSW supports CPython 3.11 onwards. The project states that it stays up to date with both SQLite and Python.

Should I use APSW or the builtin sqlite3 module?

The documentation recommends the builtin sqlite3 module if you want SQLite to appear interchangeable with the other database drivers, and APSW when you want to use SQLite fully. The documentation also has a section covering the differences between the two.

What do the numbers in an APSW version mean?

The first three components are the bundled SQLite version, and the final component is the APSW suffix. The Makefile sets SQLITEVERSION and APSWSUFFIX separately, so 3.54.1.0 means SQLite 3.54.1 with APSW revision 0, and upgrading APSW also upgrades SQLite.

What licence is APSW under?

The repository's licence field says Other, because the LICENSE file is a custom document that GitHub cannot classify. The README describes it as, in essence, any OSI approved open source licence, so the file itself should be read for the exact terms.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. rogerbinns/apsw on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rogerbinns-apsw.svg)](https://hysenlabs.com/projects/rogerbinns-apsw)

Was this useful?