Every modern web app eventually hits the moment it needs search. And almost universally, the playbook looks the same:
A few minutes later you have an 80MB virtual environment, a daemon process running somewhere, network socket overhead, and a software supply chain that just grew five links longer — all to search a few hundred markdown files or API docs.
For the Zero Dependency Hackathon 2026 (Track F: Wildcard), I set out to prove something simpler: the Python 3.14 standard library already has everything you need to build a fast, local, embeddable search engine from scratch.
The result is SwiftSearch — no pip installs, no lockfiles, no external daemons. Just python -m swiftsearch serve and it runs.
Here's what it actually took to replace the standard stack, the stdlib corner that saved the weekend, and the quirks the docs conveniently don't mention.
| Normally you'd reach for... | SwiftSearch uses... | |---|---| | Elasticsearch / Whoosh | collections.defaultdict inverted index | | nltk (tokenization) | unicodedata.normalize() | | FastAPI + uvicorn | http.server | | BM25 ranking config | A 4-term linear scoring formula | | requirements.txt | Nothing. It's empty. | Replacing Whoosh & Elasticsearch: The Inverted Index
Under the hood, full-text retrieval boils down to two primitives: tokenizing text and building an inverted index. That's it — the rest is optimization.
Instead of pulling in a dedicated indexing library, the core engine runs entirely on collections.defaultdict:
By keeping postings and document frequencies in memory, a query like python backend never scans every document sequentially. The engine jumps straight to the relevant postings lists and computes the union/intersection in sub-millisecond time.
Rather than wiring up BM25 configuration knobs, SwiftSearch scores results with a formula you can read in one line:
No tuning a k1 or b parameter you don't fully understand. A term in the title will always outrank the same term buried on page four — and you can explain why to anyone who asks. The Stdlib Corner That Saved the Weekend: unicodedata
The usual excuse for reaching for nltk or a heavy regex tokenizer library is Unicode handling — accents, umlauts, non-ASCII input that breaks naive string slicing.
Turns out unicodedata, sitting quietly in the standard library, handles all of it:
Twenty lines. No compiled regex, no backtracking overhead, no external dependency — and it splits cleanly on punctuation while staying Unicode-safe. What Turned Out Harder Than the Docs Made It Look
Everyone reaches for FastAPI or Flask when they think "Python web API." http.server has a reputation for being "not for production" — and building an embeddable engine on top of it surfaced exactly why, in two specific ways.
Dual routing (API + static assets). SimpleHTTPRequestHandler serves files; BaseHTTPRequestHandler handles custom routes. Getting GET /search?q=... to return raw JSON while GET / serves the command-palette demo — without tripping CORS — meant manually intercepting paths and setting headers by hand:
The shortcut collision. In the frontend, pressing / was supposed to focus the search palette — a common command-palette pattern. But browsers fire keydown before the input gains focus, so the literal / character leaked straight into the query string. Every shortcut press sent a request like /search?q=%2Fauth. The fix was one line — e.preventDefault() on the global listener — but finding it wasn't. The Zero-Dependency Receipt
Every import resolved to a Python 3.14 built-in: argparse, collections, dataclasses, html, http.server, json, pathlib, time, unicodedata, urllib.parse.
