Skip to content
ccrawl

API server

Serve a local index and the host graph over HTTP, on loopback, for your own use.

ccrawl api starts a small HTTP server over your local index and the web-graph host table.

It is a local exploration tool. It has no authentication, no rate limiting, no request log and no pagination, and it has never been run against hostile traffic. It binds 127.0.0.1 by default and warns on stderr if you point it at an address other machines can reach. If it has to be reachable, put it behind a reverse proxy that adds auth, and treat everything below as an internal API.

Starting the server

ccrawl api
ccrawl api --index-dir idx/                        # with full-text search
ccrawl api --addr 127.0.0.1:9090 --index-dir idx/  # a different port

Flags:

Flag Default Purpose
--addr 127.0.0.1:8080 Listen address
--index-dir none Index directory built by index build; without it /v2/search answers 503

On startup the server reads the top million hosts of the web-graph rank table into memory. That takes a minute or two on a good connection and happens on every start, because nothing is persisted. A rank table that fails to load is fatal: the server exits rather than answering host queries from half a table.

Endpoints

GET /v2/search

Run a BM25 query against the local index. Requires --index-dir, otherwise 503.

GET /v2/search?q=golang+concurrency&k=10

Parameters:

Parameter Default Description
q required Query string; missing or empty is a 400
k 10 Results to return, capped at 100

BM25 k1 and b are fixed at 1.2 and 0.75 and are not settable over HTTP. Query terms are ORed, as in ccrawl index search.

Response:

{
  "query": "golang concurrency",
  "results": [
    { "doc_id": 123, "url": "https://...", "host": "...", "title": "...", "snippet": "...", "score": 12.4, "language": "eng" }
  ]
}

GET /v2/host/{host}

Look up a single host record. Returns the same HostRecord structure host enrich emits, though over this server it carries only what the rank table holds. A host that is not in the top million is a 404.

GET /v2/host/golang.org

GET /v2/hosts

The top hosts by harmonic centrality, in rank order.

GET /v2/hosts?tld=gov&n=20

Parameters:

Parameter Default Description
tld — Restrict to one top-level domain
n 100 Hosts to return, capped at 10000

There is no cursor. Asking for more than the cap means restarting from the top with a different filter.

GET /v2/health

Always 200 while the process is up, and says which stores are loaded:

{"status":"ok","hosts":true,"search":false}

search: false means no --index-dir was given, so /v2/search will answer 503. Health is a liveness check, not a readiness check: the server does not start serving until both stores are settled, so there is no window where it is up and still loading.

Example: curl

ccrawl api --index-dir ~/cc-index/ &

curl "http://127.0.0.1:8080/v2/search?q=machine+learning&k=5" | jq .
curl "http://127.0.0.1:8080/v2/host/arxiv.org" | jq .
curl "http://127.0.0.1:8080/v2/health"

Example: integrate with a script

import requests

BASE = "http://127.0.0.1:8080/v2"

def search(q, k=10):
    r = requests.get(f"{BASE}/search", params={"q": q, "k": k})
    r.raise_for_status()
    return r.json()["results"]

for hit in search("python async programming"):
    print(hit["score"], hit["url"])

What it is not

No auth, no rate limiting, no request log, no pagination cursor, no persistence, and the search side inherits the corpus ceiling of the index: a few hundred thousand documents on an ordinary machine. It is the right tool for poking at a corpus you just built, and the wrong tool for anything with users on it.