sqlite-vec: Vector Search in SQLite (Tested Python Example)

Semantic search doesn’t need a separate vector database when the data already lives in SQLite. The sqlite-vec extension adds vector search to an ordinary SQLite file, and in this article I’ll walk you through installing it in Python and running a nearest-neighbour search.

TLDR

  • sqlite-vec is a SQLite extension for vector search. You store vectors in a vec0 virtual table and query them with MATCH.
  • In Python: pip install sqlite-vec, then sqlite_vec.load(db) on each connection.
  • Find the nearest vectors with WHERE embedding MATCH ? AND k = 2 ORDER BY distance.
  • It’s pre-v1, so pin the version you test with.
  • Without the extension, the file still opens. Only the vec0 table fails, with a clear error.

What is sqlite-vec, and what will we build?

sqlite-vec is a loadable extension that stores vectors in vec0 virtual tables and finds the nearest ones with a MATCH query. Its README describes it as “Written in pure C, no dependencies, runs anywhere SQLite runs” (sqlite-vec README). Because it’s a loadable extension, it works with the SQLite already built into your language, as long as that build allows loading extensions. The README also warns: “sqlite-vec is a pre-v1, so expect breaking changes!”

To show you how this works, I will build a tiny notes database. Each note has a 3-dimensional vector, and we’ll search for the notes closest to a query vector. Real embeddings have hundreds of dimensions, but with three you can check the distances by hand.

Prerequisites

You need Python whose sqlite3 module can load extensions, which means enable_load_extension must exist. Not every build has it. The Homebrew Python I used does, and macOS’s built-in Python 3.9 doesn’t:

Python 3.13.14 enable_load_extension: True
Python 3.9.6 enable_load_extension: False

These are the versions I tested with:

Python 3.13.14 with SQLite 3.53.4
sqlite-vec 0.1.9
stock sqlite3 3.51.0

Building a vector search with sqlite-vec

The whole example is one script. I’ll go through it in two steps.

Step 1: Install sqlite-vec

Install the package into a virtual environment, pinned to the version on this page:

$ python3 -m venv .venv
$ .venv/bin/pip install sqlite-vec==0.1.9

Step 2: Store vectors and find the nearest ones

The script keeps normal columns in an ordinary notes table and the vectors in a vec0 table that shares its rowid. Then it runs a k-nearest-neighbour query, and a cosine-distance query for comparison:

import sqlite3
import sqlite_vec

db = sqlite3.connect("notes.db")
db.enable_load_extension(True)
sqlite_vec.load(db)                # loads the vec0 extension into this connection
db.enable_load_extension(False)
print("vec_version:", db.execute("SELECT vec_version()").fetchone()[0])

db.execute("CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT NOT NULL)")
db.execute("CREATE VIRTUAL TABLE vec_notes USING vec0(embedding float[3])")
rows = [(1, "sqlite tips", "[0.9, 0.1, 0.0]"), (2, "postgres tips", "[0.1, 0.9, 0.0]"), (3, "sqlite wal", "[0.8, 0.2, 0.1]")]
for id_, body, vec in rows:
    db.execute("INSERT INTO notes (id, body) VALUES (?, ?)", (id_, body))
    db.execute("INSERT INTO vec_notes (rowid, embedding) VALUES (?, ?)", (id_, vec))
db.commit()

knn = db.execute("""
    SELECT notes.body, round(v.distance, 3) AS distance
    FROM vec_notes AS v JOIN notes ON notes.id = v.rowid
    WHERE v.embedding MATCH ? AND k = 2
    ORDER BY v.distance
""", ("[1, 0, 0]",)).fetchall()
print("knn:", knn)

cos = db.execute("""
    SELECT notes.body, round(vec_distance_cosine(v.embedding, ?), 3) AS cosine
    FROM vec_notes AS v JOIN notes ON notes.id = v.rowid ORDER BY cosine
""", ("[1, 0, 0]",)).fetchall()
print("cosine:", cos)
db.close()
$ .venv/bin/python vec_demo.py
vec_version: v0.1.9
knn: [('sqlite tips', 0.141), ('sqlite wal', 0.3)]
cosine: [('sqlite tips', 0.006), ('sqlite wal', 0.037), ('postgres tips', 0.89)]

The KNN distance is Euclidean: 0.141 is √(0.1² + 0.1²), the distance from [1, 0, 0] to [0.9, 0.1, 0.0]. vec_distance_cosine() computes cosine distance directly. That scans every row, which is fine for small tables.

Running it

The database is still a normal SQLite file. I opened it with the stock sqlite3 shell, which doesn’t have the extension. Ordinary tables work, and the vec0 table fails with a clear error instead of returning wrong results:

$ sqlite3 notes.db "SELECT body FROM notes;"
sqlite tips
postgres tips
sqlite wal
$ sqlite3 notes.db "SELECT rowid FROM vec_notes;"
Error: in prepare, no such module: vec0

Common problems

These are the problems you’re likely to hit when you first try it:

  • An AttributeError about enable_load_extension. Your Python’s sqlite3 was built without extension loading, like macOS’s built-in Python 3.9. Use a Python build that has it, such as the Homebrew Python used here:
$ /usr/bin/python3 -c "import sqlite3; sqlite3.connect(':memory:').enable_load_extension(True)"
AttributeError: 'sqlite3.Connection' object has no attribute 'enable_load_extension'
  • no such module: vec0. The connection didn’t load the extension. Call sqlite_vec.load(db) on every connection that touches the vector table.
  • An upgrade breaks the queries. sqlite-vec is pre-v1, so pin the version and re-test before upgrading.

Conclusion

sqlite-vec puts vector search inside an ordinary SQLite file. Install it with pip and load it on each connection. Then query vec0 tables with MATCH … AND k = N. The rest of the database stays readable by any SQLite tool. Compare libSQL’s vector index, which makes stock SQLite miscount rows in the indexed table and refuse inserts and deletes there.

Further reading

Tested with Python 3.13.14 (SQLite 3.53.4), sqlite-vec 0.1.9 and the sqlite3 shell 3.51.0 on 7 October 2026.