TeCoxBeta API
A self-hosted search API that plans a question, queries dozens of engines in parallel, reads the pages it finds, and returns an answer where every claim is cited.
No API keys, no signup, no per-user limits. Runs on your own server.
Overview
Send a question. TeCoxBeta breaks it into several search angles, queries up
to 48 independent engines simultaneously, merges and re-ranks the results,
opens the strongest pages to extract their real text, then writes an answer
with [n] citations mapped to source URLs.
The entire request is three fields — query, depth
and safe. Everything else is derived automatically.
Quickstart
# install and run
pip install -r requirements.txt
python run.py
Then make your first request:
curl "http://localhost:8000/search?q=how+do+vector+databases+work"
/ | Search interface |
/docs | This documentation |
/api | Interactive OpenAPI explorer |
Features
Agentic planning
Your question becomes up to 16 independent search angles covering it from every side.
Parallel retrieval
Up to 48 engines queried at once across web, news, academic, code, social and archive tiers.
Real page reading
Opens top results and extracts article text, stripping navigation, ads and cookie banners.
Inline citations
Every factual claim carries a [n] marker mapped to a real source URL.
Research agents
Up to 8 agents read separate evidence slices in parallel and report findings back.
Built-in streaming
Set stream: true on the same endpoint for live progress and token output.
Unrestricted mode
One flag opens every source tier and routes synthesis to non-refusing engines.
Token accounting
Each response reports tokens requested, used, wasted and an efficiency figure.
Self-healing
Blocked engines are detected and demoted. Losing sources lowers depth, never fails a request.
The request
The minimum viable payload:
{
"query": "how do vector databases scale to a billion vectors",
"depth": "deep",
"safe": true
}
Core fields
| Field | Type | Default | Description |
|---|---|---|---|
query | string | — | Required. Your question in plain language. |
depth | string | fast | How much work to do. See Depth. |
safe | boolean | true | false opens every source tier. |
stream | boolean | false | true returns Server-Sent Events. |
model | string | TeD | Capability tier. See Tiers. |
messages | array | [] | Prior turns. Enables follow-ups. See Site focus. |
site | string | — | Read only this host, e.g. genius.com. |
Optional fields
| Field | Type | Default | Description |
|---|---|---|---|
max_results | integer | 12 | Sources returned, 1–60. |
answer | boolean | true | false skips synthesis. |
freshness | string | — | day, week, month, year. |
language | string | en | Answer language hint. |
include_domains | array | [] | Restrict to these domains. |
exclude_domains | array | [] | Drop these domains. |
read_pages | boolean | auto | Force page reading on or off. |
follow_up | boolean | true | Allow gap-filling searches. |
cache | boolean | true | false forces a fresh run. |
Capability tiers
Three tiers, selected with the model field. Each maps to a
curated set of synthesis engines chosen and benchmarked for that role.
| Tier | Character | Best for | Pairs with |
|---|---|---|---|
| TeF | Fast and smart | Quick answers, autocomplete, chat replies | instant, fast |
| TeD | Deep and smart | Thorough research, long structured synthesis | fast, deep, extreme |
| TeM | Maximum capability | Largest context, fullest reasoning, reports | deep, extreme, ultra |
{ "query": "...", "depth": "ultra", "model": "TeM" }
TeD is the default and suits
almost everything.Depth
One knob trading time for thoroughness.
| Depth | Angles | Pages | Agents | Typical time | Answer budget |
|---|---|---|---|---|---|
instant | 1 | 0 | — | 3–6 s | 400 tokens |
fast | 3 | 5 | — | 15–30 s | 2,000 tokens |
deep | 6 | 12 | yes | 45–80 s | 7,000 tokens |
extreme | 10 | 20 | yes | 75–120 s | 12,000 tokens |
ultra | 16 | 36 | yes (8) | ~100 s | 20,000 tokens |
Answer length is budgeted in output tokens, not characters —
that is what the model actually enforces. Measured completion tokens:
instant 465, fast 1,691, deep 5,251.
Use instant for lookups, fast for chat,
deep for anything a person would open five tabs for, and
ultra when you want a written report.
Safe mode
The safe flag decides which half of the internet is in play.
| safe: true | safe: false | |
|---|---|---|
| Engines | 13–28, routed by intent | All 40+ |
| Tiers | web, news, reference, academic, code, social | plus adult, deep, archive, files |
| Synthesis | standard | non-refusing |
curl -X POST http://localhost:8000/search \
-H 'Content-Type: application/json' \
-d '{"query":"your question","depth":"deep","safe":false}'
safe: true. Retrieval and synthesis must move together —
otherwise a filtered writer sanitises an answer that retrieval already found.Streaming
Streaming is part of /search, not a separate route. Set
stream: true and the same request returns Server-Sent Events.
const es = new EventSource(
"/search?q=how+do+vector+databases+work&depth=deep&stream=true"
);
es.addEventListener("provider", e => {
const d = JSON.parse(e.data);
console.log(d.name, d.results); // an engine answered
});
es.addEventListener("read_done", e => {
const d = JSON.parse(e.data);
console.log(d.domain, d.words); // a page was read
});
es.addEventListener("token", e => {
out.textContent += JSON.parse(e.data).t; // answer, live
});
es.addEventListener("done", () => es.close());
Or with curl:
curl -N -X POST http://localhost:8000/search \
-H 'Content-Type: application/json' \
-d '{"query":"your question","stream":true}'
Downloads & files
Queries containing download intent — download,
torrent, repack, iso,
1080p and similar — are routed to the file tier first, ahead
of general web search.
Every file result carries a download object alongside the
normal source fields:
{
"id": 4,
"title": "Grand Theft Auto: The Trilogy – Definitive Edition",
"url": "magnet:?xt=urn:btih:8D003359419C09B9E72020...",
"download": {
"magnet": "magnet:?xt=urn:btih:8D0033594...",
"size": "31.8 GB",
"seeders": 364,
"infohash": "8D003359419C09B9E7202044D7F8738A9CE496E1",
"page": "https://...",
"kind": "torrent"
}
}
File results are ranked on swarm health (seeders) rather than domain authority, since a magnet link has no host to judge and no page body to read.
Site focus & conversation
A first search is necessarily shallow — snippets and partial page extracts. Site focus turns that into a complete answer by reading one source exhaustively instead of scattering across the web again.
Explicit targeting
Name a host in the query, or pass site:
{ "query": "bohemian rhapsody lyrics on genius.com" }
{ "query": "bohemian rhapsody lyrics", "site": "genius.com" }
Conversational follow-up
Pass prior turns in messages. When the new turn is a short
continuation — "give me the full lyrics", "the rest",
"read that site" — the engine returns to the source it cited last
time and reads it in full.
{
"query": "give me the full lyrics",
"depth": "deep",
"model": "TeM",
"messages": [
{ "role": "user", "content": "lyrics of Bohemian Rhapsody" },
{ "role": "assistant", "content": "...",
"sources": [ /* the sources array from turn 1 */ ] }
]
}
Content-aware targeting
For verbatim content the engine looks for the site that hosts it, not the highest-ranked article discussing it. A search for song lyrics surfaces magazine features; the follow-up needs the lyrics page itself. Recognised kinds: lyrics, recipes, code, documentation, transcripts, poems.
Verbatim extraction
Generic article extractors strip short repeated lines as boilerplate — which deletes exactly the content being asked for. Focus mode reads structured containers directly, preserving line breaks, verses, ingredient lists and code blocks as written.
Response field
"focus": {
"site": "genius.com",
"mode": "follow_up", // explicit | query | follow_up
"pages_read": 1,
"words_read": 1746
}
focus.fallback says so when it happens.Endpoints
Query parameters
GET /search?q=your+question&depth=deep&safe=false&stream=true&model=TeM
q is the question; the rest mirror the JSON fields.
Response
{
"query": "how do vector databases work",
"depth": "deep",
"answer": "Vector databases index embeddings using HNSW graphs [1]...",
"key_points": ["HNSW gives logarithmic search time [1]"],
"follow_ups": ["How does IVF compare to HNSW?"],
"sources": [
{
"id": 1,
"url": "https://example.com/hnsw",
"title": "HNSW explained",
"domain": "example.com",
"score": 3.41,
"read": true,
"words": 1240
}
],
"stats": { "candidates_found": 167, "pages_read": 12 },
"tokens": { "tokens_used": 14325, "efficiency_pct": 100.0 },
"elapsed_ms": 48213.5
}
The [n] markers inside answer match each source's
id. That mapping is how you render clickable citations.
Stream events
| Event | Fires when | Payload |
|---|---|---|
start | Request accepted | query, depth |
stage | A phase begins | phase, text |
plan | Angles decided | sub_queries |
provider | An engine answers | name, results |
search | All engines done | candidates, raw |
rank | Shortlist built | shortlist |
reading | A page is opened | domain, url |
read_done | Page text extracted | domain, words, ok |
agents | Agents report | count |
sources | Final sources chosen | sources |
token | Each answer fragment | t |
answer | Answer complete | answer, ms |
extras | Key points ready | key_points, follow_ups |
done | Finished | elapsed_ms, tokens, stats |
Token accounting
Every response reports what it cost.
| Field | Meaning |
|---|---|
tokens_requested | Sent upstream, including failed calls |
tokens_used | Actually processed (prompt + completion) |
tokens_prompt | Input half |
tokens_completion | Generated half |
tokens_wasted | Spent on calls returning nothing |
efficiency_pct | used ÷ requested |
per_engine | Per-tier breakdown |
curl http://localhost:8000/usage
Errors
| Code | Meaning | Resolution |
|---|---|---|
200 | Success | — |
401 | Bad or missing API key | Send the X-API-Key header |
422 | Invalid payload | Check query is present |
429 | Rate limited | Set RATE_LIMIT_PER_MIN=0 |
500 | Pipeline failure | Check /health and logs |
Partial failure is normal. If some engines are blocked the request still
succeeds using whoever answered. An empty answer with populated
sources means retrieval worked but synthesis did not.
How it works
PLAN question becomes several independent search angles
SEARCH all angles across all engines, fully parallel
FUSE deduplicate by canonical URL, merge rankings
RANK relevance + authority + freshness + corroboration
READ ┐ fetch top pages, strip boilerplate
RERANK ┘ score relevance (these two run concurrently)
HOP find evidence gaps, search again (deep and above)
AGENTS up to 8 readers work separate slices in parallel
WRITE cited answer, streamed as it is written
Three overlaps keep it quick: planning runs alongside the first search wave, page reading runs alongside relevance scoring, and every engine is queried concurrently rather than in sequence.
Source engines
| Tier | Count | Used for |
|---|---|---|
| Web | 12 | General search, including independent crawlers |
| Adult | 17 | Explicit queries |
| Academic | 4 | Papers, journals, medical literature |
| Deep | 3 | Hidden-service indexes via clearnet gateways |
| Social | 3 | Forums, aggregators, discussion |
| News | 2 | Current events |
| Code | 2 | Programming Q&A and repositories |
| Archive | 2 | Historical snapshots, books |
| Files | 8 | Torrent meta-indexes, repack and direct-download sites |
| Reference | 1 | Encyclopedic background |
curl http://localhost:8000/providers
Free engines rate-limit by IP and each indexes a different slice of the web. Querying many at once and fusing the rankings means one blocked engine costs nothing.
Deployment
cPanel
Ships with passenger_wsgi.py, so cPanel's Setup Python App runs
it directly.
- Upload and extract the archive, for example to
~/tecoxbeta. - cPanel → Setup Python App → Create Application.
- Python 3.9+ · Application root
tecoxbeta· Startup filepassenger_wsgi.py· Entry pointapplication. - Open the virtualenv command shown at the top of that page, then run:
pip install -r requirements.txt - Press Restart and visit your domain.
Anywhere else
python run.py
uvicorn deepsearch.api:app --host 0.0.0.0 --port 8000
gunicorn -k uvicorn.workers.UvicornWorker deepsearch.api:app -b 0.0.0.0:8000
Configuration
Copy .env.example to .env. Everything is optional.
| Variable | Default | Purpose |
|---|---|---|
PORT | 8000 | Listening port |
DEEPSEARCH_DEPTH | fast | Default depth |
RATE_LIMIT_PER_MIN | 0 | 0 means unlimited |
DEEPSEARCH_API_KEY | — | Require an X-API-Key header |
SEARCH_CONCURRENCY | 32 | Parallel engine requests |
SCRAPE_CONCURRENCY | 20 | Parallel page reads |
PROVIDER_TIMEOUT | 8 | Seconds before an engine is skipped |
CACHE_ENABLED | true | Master cache switch |
W_AUTHORITY | 0.55 | Any ranking weight is tunable |
Restricting access
# .env
DEEPSEARCH_API_KEY=your-secret-here
curl -H "X-API-Key: your-secret-here" "https://you.com/search?q=test"
FAQ
Do I need any API keys?
No. Search and synthesis both run keyless. The only optional key is your own
DEEPSEARCH_API_KEY to restrict who may call your server.
Is there a usage limit?
None. Rate limiting ships disabled — raise
RATE_LIMIT_PER_MIN above zero only if you want a cap.
Why did a search return fewer sources than usual?
Free engines rate-limit by IP. Blocked engines are detected, demoted and
skipped; the request still completes. /health shows which are
currently paused.
Can I use only specific engines?
Yes — pass providers: ["wikipedia","arxiv"] in the JSON body.
How do I make it faster?
Use depth: "instant" or "fast" with
model: "TeF", lower max_results, or set
read_pages: false to answer from result snippets.
Does it work behind a reverse proxy?
Yes, but disable response buffering for streaming or events arrive in one
lump at the end. TeCoxBeta already sends X-Accel-Buffering: no
for nginx.
Where is data stored?
Nowhere permanent. Caches are in memory and clear on restart. No search history is written to disk.