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.
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.
no-referrer, so it receives
nothing.Referrer-Policy: no-referrer, so when you click a result, the destination site is
told nothing about what you searched for or that you came from cl0q. Most search engines leak
the referring URL here.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.
Two features necessarily store data, because they cannot work otherwise. Both are opt-in and neither is required to use cl0q:
/api/watch) store the query you asked to be
alerted about, plus the browser push endpoint to notify. That is the entire point of the
feature. Deleting the subscription removes it./api/signup) optionally accepts an email address
so we can contact you about the key. It is optional — leave it blank and no contact information
is stored at all. The key itself is always stored hashed.See also the privacy policy.
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'
Access-Control-Allow-Origin: *, so you can call them from a browser. Everything is
served over HTTPS with HSTS.GET /search no key required
| Param | Type | Default | Description |
|---|---|---|---|
q | string | — | Required. 2–512 characters. May contain operators. Longer values are truncated at 512. |
limit | int | 50 | Results per page. Clamped to 1–200. |
offset | int | 0 | Pagination offset. Clamped to 0–1000000. |
lang | string | — | 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. |
safe | 0/1 | 1 |
SafeSearch (mature-content filter). On by default; pass safe=0 to disable. |
apex_only | 0/1 | 0 |
Return only apex domains, excluding subdomains. www. counts as apex. Useful
because large hosting platforms can otherwise flood results with subdomains. |
include_favicon | 0/1 | 0 |
Include the site's favicon bytes (base64) per result. Increases response size noticeably. |
include_org | 0/1 | 0 |
Include network/ASN and DNS detail per result. |
since | RFC 3339 | — | Only results crawled at or after this timestamp, e.g. 2026-01-01T00:00:00Z.
Invalid values are ignored rather than rejected. |
curl 'https://cl0q.com/search?q=intitle:handbook+rust&limit=10&apex_only=1&lang=en'
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.
Operators are parsed out of q; whatever remains is full-text matched. Prefix any
operator with - to negate it. Operators can be combined.
| Operator | Matches | Example |
|---|---|---|
site: | Domain contains | site:github.io static site |
inurl: | Domain contains (alias of site:) | inurl:docs api |
intitle: | Page title contains | intitle:changelog |
ip: | Resolved IPv4 of the host | ip:93.184.216.34 |
cname: | CNAME target | cname:cdn.example.net |
asn: | Network/ASN org, prefix match | asn:AS13335 |
org: | Network org, substring match | org:cloudflare |
favicon: / icon: | Favicon SHA-256 hash | favicon:<hex> |
org: requires at least 3 characters. Shorter values are rejected
with a 400 rather than silently running an unindexed scan — a deliberate choice to
keep one query from degrading the service for everyone.ip:, cname:, asn:,
org:, favicon:) are feature-detected at startup. If one isn't
available on this instance it returns a clear 400 saying so, instead of pretending
to work.GET /suggest no key required
| Param | Description |
|---|---|
q | Partial query. |
format | os 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.
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.
| Param | Description |
|---|---|
domain | Look up this domain's favicon hash, plus how many other domains serve the same icon and a sample of them. |
hash | Reverse pivot: hex SHA-256 → every indexed domain serving that exact icon. |
limit | Default 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.
GET /api/subdomains API key required
| Param | Description |
|---|---|
domain | Apex domain to enumerate. |
limit | Default 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.
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.
Header X-API-Key: cl0q_... (preferred) or query param ?key=cl0q_....
| Endpoint | Unauthenticated | With a free key |
|---|---|---|
/search | 10 requests per 10s per IP | Gate bypassed |
/suggest | Per-minute per-IP limit | — |
/api/favicon, /api/subdomains | Not available | 30/min, 1000/day |
/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.
| Endpoint | Description |
|---|---|
GET /healthz | Liveness probe. |
GET /api/city | ASN "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.xml | OpenSearch descriptor — add cl0q to your browser. |
POST /api/watch | Saved-search alerts via Web Push. Opt-in; stores the query and push endpoint. |
POST /api/feedback | Send feedback. |
POST /api/report | Report content or request removal of a domain. |
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.
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.
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:
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.
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.
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.
Matching is PostgreSQL full-text search over a weighted vector, not a bag of words. Fields are weighted by trustworthiness:
| Weight | Field | Rationale |
|---|---|---|
| A (highest) | title, h1 | What the page says it is. |
| B | description | Author-written summary. |
| C (lowest) | keywords | Historically spam-prone — deliberately weakest. |
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.
If cl0q's crawler visits you, here's exactly what it does and how to control 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.
robots.txt is respected. Disallow the crawler and it stops.429 and 503 are honoured, including
Retry-After. Throttling is treated as "come back later", not as a dead page./api/report to request removal of a domain.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.
| Status | Meaning |
|---|---|
400 | Bad request — q missing or under 2 chars;
org: under 3 chars; malformed favicon hash; an operator not enabled on this instance. |
401 / 403 | Missing or invalid API key on a gated endpoint. |
405 | Wrong method (e.g. GET on /api/signup). |
429 | Rate limited. Back off, or use an API key. |
500 | Server error. These are reported internally; if one is
reproducible, please send it via /api/feedback. |
503 | Backend temporarily unavailable — returns a maintenance response rather than hanging. |