Relevance
Search results are often only loosely about your query. relevance=score adds an on_topic probability to every row, judged against the query you searched for. relevance=filter does that and also drops the rows that fall below a threshold, telling you exactly which ones it dropped.
Like labels, the score is a signal, not a verdict, and a row that could not be judged is never dropped.
When to use it#
- A broad search returns noise and you would rather not pay for, store or read the off-topic rows.
- You want to rank or cut on a number you control (
threshold), not on a yes or no. - You run multi-platform search and want each platform's off-topic rows removed before the lists are fused.
The request#
Add relevance=filter to a search endpoint. The query it judges against is the one you searched for.
curl "https://api.onekey.dev/v1/hackernews/search?q=rust+compiler&relevance=filter&threshold=0.5" \
-H "x-api-key: YOUR_API_KEY"| Parameter | Values | What it does |
|---|---|---|
relevance | score or filter | score adds computed.relevance.on_topic to each row and drops nothing. filter also drops rows below threshold. |
threshold | 0 to 1, default 0.5 | The cut-off for filter. Only valid together with relevance; otherwise it is a free 422 INVALID_PARAMS. |
strict | true (default) or false | As on Labels. |
You can combine it with label= or include=labels and themes=true on the same call. Relevance runs first, so labels and themes are computed only over the rows that were kept, and you are not charged to label rows that were dropped.
Where it works#
Relevance needs a query to judge against, so it is accepted on the search endpoints that also accept enrichment (7 endpoints on 7 platforms, generated from the specs) and on /v1/search/multi:
bluesky.searchdevto.searchgitlab.searchhackernews.searchlemmy.search_postsreddit.search_postsstackexchange.search
The full opt-in table, with every other endpoint that accepts labels and themes, is on Labels. relevance on any other endpoint is a free 422 INVALID_PARAMS.
The response#
Each row gets computed.relevance. The relevance summary sits at the top level next to data, and dropped and dropped_ids are repeated beside it for convenience. Recorded on the pre-release test provider (rows shortened):
{
"success": true,
"data": [
{
"content": { "title": "Writing a Rust compiler in C", "…": "…" },
"computed": { "relevance": { "on_topic": 1.0 } }
}
],
"relevance": {
"query": "rust compiler",
"threshold": 0.5,
"status": "complete",
"rows": 10,
"labelled": 10,
"dropped": 0,
"dropped_ids": [],
"extra_credits": 4.969,
"methodology_version": "fake/1"
},
"dropped": 0,
"dropped_ids": [],
"metadata": { "credits_charged": 9.969, "extra_credits": 4.969, "…": "…" },
"error": null
}The summary has the same counters as the labels summary (rows, labelled, cached, skipped, unjudged, status, reason, methodology_version) plus:
| Field | Meaning |
|---|---|
query | The text the rows were judged against. |
threshold | The cut-off used. |
dropped | How many rows filter removed. |
dropped_ids | Their ids, so you can tell exactly what is missing. For a search result the id is the record's own id; a row without one is row-<index>. Only present with filter. |
computed.relevance is {"on_topic": p} with p from 0 to 1, or {"on_topic": null} for a row that was not judged.
How to read it honestly#
- A row that was not judged is kept.
on_topic: nullis "unknown", and unknown rows are never dropped, so a provider failure part way cannot silently empty your results. Checkunjudgedandstatus. dropped_idsis the audit trail. Removing rows is the only place enrichment changes what you get back, so it always lists them. Keep them if you need to show what was excluded.- A threshold is your decision. 0.5 is only a default. A higher threshold drops more, and a probability is not a guarantee that a kept row is on topic or that a dropped one was not.
- Judged on the text in the row. The title or text field named in the opt-in table is what is read, not the page behind a link.
methodology_versionnames the method, as on Labels. Judgments from the pre-release test provider are a keyword heuristic, not model quality.
Multi-platform search#
On /v1/search/multi, relevance is applied to each platform's rows separately, before any fuse=rrf step, so the fused list is built only from kept rows. Each platform's row carries its own relevance, dropped and dropped_ids (ids there are prefixed platform:), and data.relevance adds the legs up. A platform whose search endpoint does not accept enrichment is listed under unsupported, and its rows come back untouched and are never dropped.
What it costs#
A separate charge for each judged row, on top of the search's own price. Rows that were not judged, and texts already judged for the same query in the last 30 days, are free. See AI pricing.
Errors it can return#
| Status | Type | When |
|---|---|---|
| 422 | INVALID_PARAMS | relevance is not score or filter, threshold is outside 0 to 1 or given without relevance, or the endpoint is not a search that accepts enrichment. Free. |
| 429 | AI_BUDGET_EXCEEDED | Nothing could be judged because today's allowance is used up and strict=true. |
| 503 | AI_UNAVAILABLE | Nothing could be judged because enrichment is off or unavailable and strict=true. |
| 402 | INSUFFICIENT_CREDITS, KEY_BUDGET_EXCEEDED | The reservation for the judgments cannot be covered. |
See Errors for the full list.