Base URL https://api.wuzzy.io. JSON in, JSON out. Failures are {"error": "..."} with the
status code carrying the meaning.
| Method | Path | Gate |
|---|---|---|
POST |
/search |
x402 payment |
GET |
/indexes |
open |
GET |
/indexes/:reference |
open |
POST |
/indexes |
x402 payment |
POST |
/indexes/:reference/urls |
x402 payment, owner only |
DELETE |
/indexes/:reference |
x402 signature, owner only |
GET |
/healthz |
open |
:reference is an index id or its slug; both resolve.
| Field | Type | Default | Notes |
|---|---|---|---|
query |
string | required | Blank gives 400 |
topK |
number | 10 |
Clamped to 1..50 |
offset |
number | 0 |
Documents to skip |
mode |
string | hybrid |
hybrid, vector or lexical. A tuning aid |
index |
string | global | Index id or slug |
| Field | Meaning |
|---|---|
total |
Documents found within the retrieval window. A floor, not a count, when exhaustive is false |
exhaustive |
Whether both arms ran out of matches before the window filled |
hasMore |
Whether another page exists. Page with this, not with total |
score |
Reciprocal Rank Fusion. Small, and comparable only within one response |
ranks |
Where each arm placed the result. A tuning aid, not a contract |
provenance.attestationUid |
null until the document has been attested |
provenance.contentHash |
sha256 of the canonicalized text. Reproduce it with the v1 procedure |
provenance.rawHash |
sha256 of the bytes the origin served, before canonicalization. What you compare against your own fetch while a result is still unattested |
mode |
Which retrieval actually ran for this response: hybrid, vector or lexical |
Retrieval is hybrid where a deployment has an embedding provider: BM25 and vector similarity run independently and are fused by rank rather than by score, because BM25 is an unbounded sum and cosine is bounded, and normalizing between them would change meaning as the corpus grows.
Check mode on the response rather than trusting this page. A deployment configured
without an embedding provider serves lexical only, and the difference shows up as ranking
quality rather than as an error: coverage stays good, ordering gets literal. Every response
says which retrieval actually ran.
The retrieval window is fixed per query and does not grow with offset, so pages cannot
reorder under a reader between requests.
The public catalog, global first. Unlisted indexes are absent entirely.
The same fields plus live crawl progress. 404 if nothing resolves.
status is derived from the crawl queue rather than stored, so it cannot disagree with the
work outstanding. pages counts membership rows, attestations how many carry a UID, and
pending how many paid-for URLs the store does not hold yet. Queue rows are retired as each
page lands, so pending falls during a crawl rather than dropping all at once at the end.
failed counts URLs that were paid for, fetched, and produced nothing indexable: a 4xx, a
robots refusal, or a page too thin to extract. failures names them with the reason, capped at
50, because an index that is short of what was bought should say which pages and why rather
than leaving you to diff a sitemap against your results. A failed URL is not refunded and not
retried automatically; commissioning it again is the retry.
See Commission an index for the fields and the reasoning. Returns 201 with
the status report. A request carrying more URLs than one request may hold returns 400, with
the limit and what you asked for, so a client can split the list and retry:
Appends. Owner only. Returns the status report merged with what the URLs did:
joined were already in the shared store and became members immediately, with no crawl.
enqueued were unknown and were queued. This split is where "crawled once, shared by every
index that wants it" actually happens, and it is why you are not billed for a second copy.
Owner only. Removes the index and its membership rows. The underlying documents are untouched, because other indexes may hold them and the provenance trail is append-only.
Deletion is free. It still requires a valid payment, in PAYMENT-SIGNATURE or X-PAYMENT,
purely as a signature proving who is asking; nothing is settled.
| Code | Meaning |
|---|---|
200 |
Fine |
201 |
Index created |
400 |
Blank query, malformed urls, too many URLs for one request, bad wallet or URL |
402 |
Payment required, absent, malformed or unmatched. Version 2 requirements in the PAYMENT-REQUIRED header, version 1 accepts in the body |
403 |
Verified payer may not read this index, or is not the owner. Never charged |
404 |
No such index |
429 |
Rate limited. Carries Retry-After |
403 costs nothing.PAYMENT-SIGNATURE for version 2, X-PAYMENT for version 1.amount, which version 1 calls maxAmountRequired, is in USDC atomic units, six decimals.The in-repo reference at apps/backend/README.md covers the same routes plus operator concerns such as configuration and the admin surface.