Get Started

Build a real search engine from scratch

Build a working search engine in pure Python. A polite crawler gathers a corpus, a tokeniser and Porter stemmer turn text into index terms, an inverted index makes lookup instant, and BM25 ranks the matches the way Elasticsearch does. Index thousands of real documents, then query them (phrases, AND, ranking) and get relevant results in milliseconds. By the end you know why BM25 beats counting words, and how Lucene scales the same ideas to billions of documents.

115 minAdvancedTools: Free. Pure Python standard library, nothing to install and no API keys.Step 1 free
Tested 12 Oct 2026 · Python 3.13.5, standard library only (no third-party packages), a 5,264-document corpus built from ten public-domain Project Gutenberg books, Windows 11
A working search engine in pure Python that indexes thousands of real documents and returns ranked results in a few milliseconds, queried by phrase, by AND, or ranked across the whole corpus
The four parts built from scratch and understooda polite crawler, a tokeniser with a Porter stemmer, a positional inverted index, and BM25 ranking, with a measured comparison showing why BM25 beats counting words
A starter kit with every file and a ready-made corpus, so you can run the finished engine first and then rebuild each part, plus a benchmark that proves one change makes queries about 50 times faster
Before you start
  • Level 0 done: The terminal survival kit and Install anything from GitHub, safely. You run everything here from a terminal.
  • Python 3.10 or newer. Check with python --version. If you did Build your own API in Python you already have uv, which gives you a clean Python with one command; plain python works perfectly well here.
  • A little Python: functions, dicts and lists. You do not need to know anything about search, stemming or ranking. Every file is complete and explained, and the maths has worked examples.
  • A code editor. VS Code ↗ is free; any editor works.
01/07What a search engine is made of0 done
STEP 01 of 07~10 min

What a search engine is made of

Four parts turn a pile of text into ranked answers. Seeing them first means every later step has a place to fit, and you can run the finished engine today to see where you are heading.

  1. Type two words into a search box and a good engine returns the most relevant document out of millions, in the time it takes the page to paint. It does that with four parts, and you will build each one:

    • A corpus: the set of documents you search. Step 2 gathers one.
    • Tokenisation: turning each document's text into a clean list of index terms. Step 3.
    • The inverted index: a lookup from each term to the documents that contain it, so a query reads a few short lists instead of every document. Step 3.
    • Ranking: scoring the matches so the best one comes first. Counting word matches is the naive way; BM25 is the way real engines do it. Step 4.
  2. Two of those words carry the whole idea. To index a corpus is to read every document once, up front, and build the lookup structure. To crawl is to gather the documents in the first place (following links, fetching pages). You crawl once to get a corpus, index it once, then answer many queries fast against the index.

  3. Why not loop over every document for each query and count the matches? That is a full scan, and it is what grep or SQL LIKE do. It is fine for a folder of notes and hopeless for a million documents: the work grows with the size of the corpus, on every single query. An inverted index moves that work to indexing time, so a query touches only the terms it asks about. Ranking is the second half of the problem: once you have the matches, which one is actually most relevant?

  4. Set up a folder and grab the kit. Every file is in search-engine.zip (2MB), including a ready-made corpus so you can see the goal straight away:

    Terminal
    mkdir search-engine
    cd search-engine
    # unzip search-engine.zip into this folder (the files sit directly inside it)
  5. Run the finished engine once, to see what you are building. It loads the corpus, indexes it, and answers a fixed set of queries:

    Terminal
    python engine.py demo
    Output
    indexed 5,264 documents, 16,464 unique terms, avg length 85.5 tokens, in 1870 ms
    
    query: 'white whale'   terms=['white', 'whale']   scorer=BM25
      1. [ 8.797] Moby Dick #353
      2. [ 8.119] Moby Dick #851
      3. [ 7.943] Moby Dick #400
      4. [ 7.563] Moby Dick #379
      5. [ 7.413] Moby Dick #918
      (936 ranked, 49.98 ms)
  6. That is the destination: 5,264 documents indexed in under two seconds, and a two-word query ranked across all of them with real relevance scores. Now close the kit and build it yourself, one part at a time. You can always compare against the kit's version.

    TipShort on time? Steps 2 to 4 give you a corpus, an index and BM25 ranking: a search engine that works. Steps 5 to 7 (phrase and AND queries, speed, and how the big engines scale) build on that and can wait for another evening.
✓
Checkpointpython engine.py demo prints indexing stats and a ranked list of results with scores. You can name the four parts and say what the inverted index saves you from.
If it breaks'python' is not recognised + 2 more
'python' is not recognised
Install Python 3.10+ from python.org ↗ and tick 'Add python.exe to PATH', or use uv run python ... if you have uv from the API lesson. On some systems the command is python3.
'can't open file engine.py'
You are not in the folder that holds the files. The kit's files sit directly inside the folder, not in a sub-folder. Run cd search-engine and check with dir (Windows) or ls (Mac and Linux).
'FileNotFoundError: corpus.jsonl'
corpus.jsonl must be in the same folder as engine.py. It is in the kit. Step 2 also builds it from scratch.
Still stuck? Ask the community
Next mission · Advanced buildsBuild Google-Docs-style live collaboration with CRDTs
→