This web search API quickstart is for developers who want web search results inside their own app without building a crawler. Make one GET /web call, then wrap it in a tiny JSON service with one route, GET /search?q=, that returns clean {title, url, snippet, domain} results. It uses only the Python standard library, with Python and JavaScript snippets to start from.
The problem
Many apps need a search box that looks at the web: a research tool, an agent, a link finder in a CMS. Building and running a crawler for that is a project of its own. What most teams want is one call that returns titles, links and snippets, plus a clear price per call. Then they need the small pieces around it: input checks, paging, removing duplicate links and caching.
What the search API quickstart does
- One route,
GET /search?q=, withqfrom 1 to 300 characters andpagesfrom 1 to 3. - Returns results as
{title, url, snippet, domain, rank}. - Removes duplicate URLs (same host without
www, same path) and counts them inrepeats_dropped. - Caches each query for 10 minutes, so the same search again is free and instant.
- Rejects bad requests with HTTP 400 before any call.
- Includes a small HTML search page and a CLI.
How it works
- Check the query. Not blank, at most 300 characters,
pages1 to 3. - Search the web.
GET /webwithqueryandstart. Page 1 isstart=0, page 2 isstart=20, page 3 isstart=40. - Clean up. Turn each result into
{title, url, snippet, domain, rank}and drop repeated URLs. - Cache and serve. Store the result for 10 minutes and return JSON with the count, cost and latency.
The code
The core call, copied from the example's README. The key is read from the DESEARCH_API_KEY env var:
import json, os, urllib.parse, urllib.request
url = "https://api.desearch.ai/web?" + urllib.parse.urlencode({"query": "bittensor subnet", "start": 0})
req = urllib.request.Request(url, headers={"Authorization": os.environ["DESEARCH_API_KEY"]})
with urllib.request.urlopen(req, timeout=60) as r:
print(r.headers.get("X-Desearch-Cost-Usd"))
for item in json.load(r)["data"]:
print(item["title"], item["link"])The response holds data, a list of results with title, link and snippet. The README also has the same call in curl and in JavaScript.
The folder layout:
core.py: query check, paging, result cleanup, dedupe, cache and HTML. No network.app.py: the HTTP service, the CLI and the Desearch client.test_core.py: offline tests.samples/: one/searchresponse and the matching HTML page.
Endpoints
GET /web
Example output
Saved sample from a run on 2026-10-16 at 15:14 (Asia/Tbilisi) for GET /search?q=bittensor+subnet&pages=2. This page shows that stored request and response. It does not run a live demo.
Sample request
GET /search?q=bittensor+subnet&pages=2That service call runs two GET /web requests. Page 1 is start=0 and page 2 is start=20.
Sample response
| What came back | Count |
|---|---|
| Desearch calls | 2 |
| Results per page | 19 and 20 |
| Repeated URLs dropped | 2 |
| Results returned | 37 |
| Time for both calls | 8.87 s |
| Same request from the cache | about 2 ms, 0 calls |
Two of the first results, trimmed:
{"rank": 3, "domain": "bittensor.com", "title": "Running a subnet - Bittensor Docs", "url": "https://www.bittensor.com/docs/guides/subnets"}
{"rank": 5, "domain": "docs.learnbittensor.org", "title": "Subnet Configuration (Hyperparameters) | Bittensor", "url": "https://docs.learnbittensor.org/subnets/subnet-hyperparameters"}Run it yourself
git clone https://github.com/Desearch-ai/desearch-py-examples && cd desearch-py-examples/web-search-api-quickstart
export DESEARCH_API_KEY=... # from console.desearch.ai
python3 app.py --port 8000 # then: curl -s "http://127.0.0.1:8000/search?q=bittensor+subnet&pages=2"pages: 1 to 3 pages of results, one call each.--cli "bittensor subnet" --pages 2: run one search from the command line.--log-dir runs/today: save each API call with the key removed.
What it costs
Measured on 2026-10-16 from the X-Desearch-Cost-Usd header: $0.001 per GET /web call, whatever the number of results. One page is one call, so pages=2 is $0.002, and a cache hit is $0. See pricing.
Ideas to extend it
- Put the
/searchroute behind your app's search box or give it to an agent as a tool. - Add
domainfilters in your service to show results from sites you choose. - Store results per query to build a small research archive over time.
