cl0q Developer Documentation

An independent, open-source search engine — its own crawler, its own index, no third-party search backend.

cl0q crawls the open web, builds its own inverted index, and serves it from a small read-only HTTP API. It is not a front-end for anybody else's search results. This page documents the public API, the query language, how the index is built, and how cl0q's crawler behaves if it visits your site.

Contents

Privacy

cl0q is built so that there is no search history to leak, subpoena, sell, or breach. The guarantees below are architectural — they follow from how the system is built, not from a promise to behave.

Where a search query does and does not go A query is held in memory for the life of the request and returned as results. It is not written to any database, not sent to any third party, and not leaked to the site you click through to. your search query held in memory, for one request matched → ranked → answered → gone results, back to you a database no history table exists any third party zero external requests the site you click Referrer-Policy: no-referrer No account. No key. No cookie. Nothing to attach a search to in the first place. Every crossed path above is a design decision, not a setting — there is no toggle that turns them back on.
Where your query doesn't go. The third arrow is the one most search engines get wrong: when you click a result, the destination normally receives the referring URL — which contains what you searched for. cl0q sends no-referrer, so it receives nothing.

We do not track your searches

What we do keep

cl0q keeps no per-request access log. For a normal request — including every search — no IP address, requested URL, query string, or user agent is written to disk. There is no per-request record to analyse, profile, share, or sell, because none is kept.

The only things logged are application errors and operational events (a failed or slow database query, the server starting or stopping), for a short retention window used strictly for uptime monitoring and debugging. These entries contain no IP address and no search terms — a rejected query, for instance, records only its length, never its content.

Things you explicitly opt into

Two features necessarily store data, because they cannot work otherwise. Both are opt-in and neither is required to use cl0q:

See also the privacy policy.

Quick start

Search is public and needs no key:

curl 'https://cl0q.com/search?q=open+source+search&limit=5'

For the API-key-gated endpoints, mint a free key and send it as a header:

curl -X POST https://cl0q.com/api/signup
# -> {"api_key":"cl0q_...","tier":"free","per_min":30,"per_day":1000, ...}

curl -H 'X-API-Key: cl0q_...' 'https://cl0q.com/api/favicon?domain=github.com'
All endpoints are read-only, return JSON, and send Access-Control-Allow-Origin: *, so you can call them from a browser. Everything is served over HTTPS with HSTS.

GET /search no key required

Parameters

ParamTypeDefaultDescription
qstring Required. 2–512 characters. May contain operators. Longer values are truncated at 512.
limitint50Results per page. Clamped to 1–200.
offsetint0Pagination offset. Clamped to 0–1000000.
langstring Restrict to a detected page language (e.g. fr). Also selects language-aware stemming and snippet highlighting. Unknown/empty means no filter and English snippets.
safe0/11 SafeSearch (mature-content filter). On by default; pass safe=0 to disable.
apex_only0/10 Return only apex domains, excluding subdomains. www. counts as apex. Useful because large hosting platforms can otherwise flood results with subdomains.
include_favicon0/10 Include the site's favicon bytes (base64) per result. Increases response size noticeably.
include_org0/10 Include network/ASN and DNS detail per result.
sinceRFC 3339 Only results crawled at or after this timestamp, e.g. 2026-01-01T00:00:00Z. Invalid values are ignored rather than rejected.

Example

curl 'https://cl0q.com/search?q=intitle:handbook+rust&limit=10&apex_only=1&lang=en'

Response

JSON with a result array and a real total (not an estimate), so you can paginate honestly. Each result carries the indexed page metadata: domain, page title, h1, description, a match-highlighted snippet, HTTP status, and the crawl timestamp. include_org=1 adds network/ASN and DNS fields; include_favicon=1 adds icon bytes.

Only pages that returned HTTP 200 are searchable. Pages that errored, redirected to nothing, or served an anti-bot interstitial are recorded for crawl scheduling but deliberately kept out of the index — so you don't get results whose "title" is "Just a moment…".

Query operators

Operators are parsed out of q; whatever remains is full-text matched. Prefix any operator with - to negate it. Operators can be combined.

OperatorMatchesExample
site:Domain containssite:github.io static site
inurl:Domain contains (alias of site:)inurl:docs api
intitle:Page title containsintitle:changelog
ip:Resolved IPv4 of the hostip:93.184.216.34
cname:CNAME targetcname:cdn.example.net
asn:Network/ASN org, prefix matchasn:AS13335
org:Network org, substring matchorg:cloudflare
favicon: / icon:Favicon SHA-256 hashfavicon:<hex>

Notes and limits

Suggest API

GET /suggest no key required

ParamDescription
qPartial query.
formatos returns the OpenSearch suggestions array [query, [domains], [titles], [urls]]. Omit for the default JSON shape.

Built for typeahead: it's tuned for latency and rate-limited separately from /search. cl0q also ships an OpenSearch descriptor, so browsers can add cl0q as a search engine and use this endpoint for suggestions.

Favicon-hash API

GET /api/favicon API key required

Favicons are content-addressed by SHA-256, which makes them a pivot: sites sharing an icon are usually related — same operator, same framework default, same phishing kit, same rebrand. This is the same class of pivot Shodan/FOFA-style tooling uses, over cl0q's open-web index.

How the favicon pivot works Identical icon bytes hash to one SHA-256, stored once. Querying that hash returns every indexed domain serving the same icon, which reveals related infrastructure. STORING example.com shop.example.com unrelated-looking.io identical icon bytes → one SHA-256 stored ONCE content-addressed an icon shared by 100,000 sites costs one row PIVOTING /api/favicon?hash=<sha256> every indexed domain serving that icon Because storage is content-addressed anyway, the pivot is free — it's the same lookup, run backwards.
Content addressing, run backwards. Deduplicating icons by hash is a storage decision; making that hash queryable turns it into an infrastructure-mapping tool. Shared icons frequently expose relationships that DNS and WHOIS don't.
ParamDescription
domainLook up this domain's favicon hash, plus how many other domains serve the same icon and a sample of them.
hashReverse pivot: hex SHA-256 → every indexed domain serving that exact icon.
limitDefault 100, max 1000.
# forward: domain -> hash + who else uses it
curl -H 'X-API-Key: cl0q_...' 'https://cl0q.com/api/favicon?domain=github.com'

# reverse: hash -> domains
curl -H 'X-API-Key: cl0q_...' 'https://cl0q.com/api/favicon?hash=<hex-sha256>&limit=50'

Pass exactly one of domain or hash. A malformed hash returns 400.

Subdomains API

GET /api/subdomains API key required

ParamDescription
domainApex domain to enumerate.
limitDefault 100, max 1000.

Returns subdomains cl0q discovered by crawling the open web. This is passive — it reflects what was found by following public links and DNS, and is not a live brute-force of your zone.

API keys & rate limits

Getting a key

curl -X POST https://cl0q.com/api/signup
curl -X POST https://cl0q.com/api/signup -d 'email=you@example.com'   # optional

Returns the key once, in the response body. It is shown only once — cl0q stores only a hash and cannot show it to you again. The email is optional and used solely to contact you about the key; omit it and nothing identifying is stored.

Sending a key

Header X-API-Key: cl0q_... (preferred) or query param ?key=cl0q_....

Limits

EndpointUnauthenticatedWith a free key
/search10 requests per 10s per IPGate bypassed
/suggestPer-minute per-IP limit
/api/favicon, /api/subdomainsNot available 30/min, 1000/day
The /search gate exists to protect a small shared database from scrapers, not to nag humans — normal interactive searching never hits it. If you're building something real, take a free key.

Paid tiers raise per_min/per_day on the same key. During beta, everyone gets a free key immediately.

Other endpoints

EndpointDescription
GET /healthzLiveness probe.
GET /api/cityASN "skyline" — aggregate view of networks in the index. Cached.
GET /api/city/asn?org=Per-network detail windows.
GET /api/city/locate?q=Find which network a domain sits on.
GET /site/<domain>Human-readable per-domain page.
GET /network/<asn>Human-readable per-network hub page.
GET /sitemaps/Sitemap index for the above.
GET /opensearch.xmlOpenSearch descriptor — add cl0q to your browser.
POST /api/watchSaved-search alerts via Web Push. Opt-in; stores the query and push endpoint.
POST /api/feedbackSend feedback.
POST /api/reportReport content or request removal of a domain.

How the index is built

Four stages, each feeding the next. Every stage is incremental and restartable — nothing depends on a full rebuild. The important thing to notice is what crosses each boundary: a layer only ever hands the next one a small, well-defined artefact.

cl0q's four processing layers Discovery produces domain names. Page crawl turns those into page records, of which only HTTP 200 responses are indexed. Enrichment adds DNS, network and favicon facets. Serving exposes a read-only API. The read path never writes back to the crawl tables. 1 DISCOVERY Breadth-first walk from curated, link-rich seeds… …plus a rotating sample of already-known domains, re-mined for outbound links. finds what exists domain names 2 PAGE CRAWL Fetch the homepage, gate on text/html, parse metadata. Anti-bot interstitials are detected and refused rather than indexed. reads what it says page records — only HTTP 200 becomes searchable 3 ENRICHMENT Resolve DNS (A / AAAA / MX / CNAME) · map IP → network, ASN, country · fetch favicons, stored content-addressed by SHA-256 (one icon stored once). adds where it lives facets: ip: cname: asn: org: favicon: 4 SERVING Read-only API: /search · /suggest · /api/* · /site/ · /network/ Two-stage query: candidates + rank, then one batched detail fetch. answers questions the read path never writes back to the crawl tables →
The four layers. Each stage is incremental and restartable, and each hands the next a small artefact rather than sharing internals. Because serving never writes to the crawl tables, search traffic and crawl traffic can't slow each other down.

1. Discovery

A breadth-first crawler walks outward from a curated seed list of link-rich, bot-friendly pages, recording every new registrable domain it sees. Because a fixed seed list plateaus quickly — it re-walks the same neighbourhood and stops finding anything — discovery also continuously re-samples domains already known to the index and mines their homepages for outbound links. The known web becomes a rotating frontier rather than a finished list.

2. Page crawl

Discovered domains are fetched and parsed into the index: title, h1, meta description, keywords, canonical, Open Graph and Twitter card metadata, charset, detected language, robots directives, outbound link count, image count, word count, HTTP status, response latency, redirect count, and server header.

Two details matter for quality. Content-type gating means only text/html is parsed, so binary responses never become garbage "pages". And anti-bot interstitial detection means when a challenge page is served instead of real content, cl0q recognises it, refuses to index the challenge text, and records why — rather than indexing thousands of identical "Checking your browser…" pages.

Most fetches do not end up in the index. Every gate below exists to keep something out:

What happens to a fetched page A fetched page passes four gates — content type, challenge detection, blacklist, and HTTP status — before it becomes searchable. Failing a gate means it is dropped, blanked, deleted, or recorded but excluded from search. homepage fetched Content-Type is text/html? no not parsed — no binary junk pages yes anti-bot challenge page? yes content blanked, reason recorded; kept out of the index no matches content blacklist? yes removed — never enters the index no HTTP status is 200? no recorded for crawl scheduling, excluded from search yes INDEXED — searchable title · h1 · description · keywords · metadata a non-200 page is remembered so it isn't re-crawled forever — but it never reaches search
Four gates. Being remembered and being searchable are deliberately different things: the crawler keeps a record of pages that failed so it knows not to keep re-fetching them, while search only ever sees clean HTTP 200 content.

3. Enrichment

Separate passes resolve DNS (A/AAAA/MX/CNAME) for domains discovered link-only, and map IPs to their network/ASN and country. These feed the ip:, cname:, asn: and org: operators and the region facets. Favicons are fetched and stored content-addressed by SHA-256, so an icon shared by 100,000 sites is stored once — which is also what makes the favicon pivot cheap.

4. Serving

A read-only API serves the index. Nothing in the read path writes to the crawl tables, so search load and crawl load stay independent.

Federation

cl0q can accept crawl results from trusted independent nodes, so contributors can crawl on their own hardware and contribute back without ever receiving database credentials. Submissions are authenticated, re-validated server-side against central policy, and tagged with their source so any node's contribution can be audited or removed wholesale. Interested in running one? Use /api/feedback.

Ranking & matching

Matching is PostgreSQL full-text search over a weighted vector, not a bag of words. Fields are weighted by trustworthiness:

WeightFieldRationale
A (highest)title, h1What the page says it is.
BdescriptionAuthor-written summary.
C (lowest)keywordsHistorically spam-prone — deliberately weakest.
How a match is scored Title and h1 carry weight A, description weight B, and keywords weight C. An apex boost and a network popularity signal are layered on top of the text score. FIELD WEIGHTS A title what the page says it is A h1 the heading a human wrote B description author-written summary C keywords spam-prone — weakest on purpose THEN LAYERED ON + apex boost + network popularity an apex outranks a subdomain Weighting by field is why a page that merely stuffs your term into meta keywords loses to one that puts it in the title.
Scoring. Fields are weighted by how hard they are to game. Keywords are indexed but deliberately given the least influence.

Ranking then layers on an apex boost (an apex domain outranks a subdomain, all else equal) and a network popularity signal derived from how much of the index sits on a given network. Substring operators (site:, intitle:, org:) are trigram-indexed, which is why org: needs 3 characters — a shorter pattern has no trigram to look up.

Search runs in two stages: a candidate-and-rank pass, then a single batched detail fetch for the page you asked for. Hot queries are briefly cached. Result total is a true count, not an estimate.

The life of a query A query is parsed into operators plus free text, matched and ranked against the weighted index in stage one, then the requested page of results is detail-fetched in a single batch in stage two, and returned as JSON. your query ?q=… parse operators split from free text STAGE 1 weighted match + rank over the whole index → candidate domains STAGE 2 one batched fetch for just your page of results → full detail JSON results Two stages, because ranking the whole index and fetching every field are different costs. Stage 1 touches many rows and returns little; stage 2 touches few rows and returns a lot. total is a true count, not an estimate — so pagination is honest Your query exists in memory for the life of this request. When the response is written, it is gone — there is no history table for it to be written to.
Two stages. Splitting "which pages match" from "tell me everything about these 50" is what keeps a query over a multi-million-page index fast: the expensive ranking pass carries almost no data, and the wide fetch touches almost no rows.

For site owners: the cl0q crawler

If cl0q's crawler visits you, here's exactly what it does and how to control it.

Identifying it

The crawler sends a user agent containing cl0q-crawler and a link to cl0q.com/bot. It is browser-shaped so that naive filters don't reject it, but it always self-identifies and is always contactable — it will never pretend to be an ordinary browser to evade you.

Controlling it

Being crawled efficiently

The crawler sends If-None-Match / If-Modified-Since when it has previously stored an ETag or Last-Modified for you. Serve validators and a re-crawl costs you a 304 and a few bytes instead of a full page render. cl0q fetches your homepage — it does not spider your whole site.

What cl0q will not do: it does not attempt to bypass bot challenges, solve JS challenges, rotate residential proxies, or disguise its origin. If you block it, it stays blocked. Anti-bot circumvention is deliberately out of scope.

Errors

StatusMeaning
400Bad request — q missing or under 2 chars; org: under 3 chars; malformed favicon hash; an operator not enabled on this instance.
401 / 403Missing or invalid API key on a gated endpoint.
405Wrong method (e.g. GET on /api/signup).
429Rate limited. Back off, or use an API key.
500Server error. These are reported internally; if one is reproducible, please send it via /api/feedback.
503Backend temporarily unavailable — returns a maintenance response rather than hanging.