Themes
themes=true groups the rows a call returned by what they talk about and gives each group a name, a size, the ids of its rows and a sample. Ninety-seven replies to a post become a handful of topics you can read in a minute.
When to use it#
- You have a long comment or reply list and want to see the main topics before reading individual rows.
- You want counts per topic, with the row ids to jump back to the originals.
It works on the same 31 list endpoints as labels, and on multi-platform search.
The request#
curl "https://api.onekey.dev/v1/bluesky/post_replies?uri=at://did:plc:…/app.bsky.feed.post/…&themes=true" \
-H "x-api-key: YOUR_API_KEY"| Parameter | Values | What it does |
|---|---|---|
themes | true | Cluster the rows the call returns and name each cluster. |
strict | true (default) or false | As on Labels. Only matters for the naming step. |
Combine it with relevance=filter to cluster only the rows that survived the filter.
How it works#
- Clustering is deterministic and involves no model. The rows' text is compared with a standard word-frequency similarity (TF-IDF cosine) and grouped. The same rows always give the same clusters, and there are at most 12.
- Only the names come from the provider. One call names all the clusters at once. If enrichment is off or the naming fails, the clusters are still returned, named
Theme 1,Theme 2and so on, withnamed: false.
The response#
The themes block sits at the top level next to data. Recorded on the pre-release test provider (list shortened):
{
"success": true,
"data": [ "…97 replies…" ],
"themes": {
"status": "complete",
"rows": 97,
"clustered": 92,
"skipped": 5,
"named": true,
"extra_credits": 7.942,
"methodology_version": "fake/1",
"clustering": "cluster/1",
"themes": [
{ "theme": "Bsky Bluesky Social", "size": 41, "row_ids": ["at://did:plc:…/3mw2eruoksc2x", "…"], "sample": "…", "terms": ["bsky", "bluesky", "…"] }
]
},
"metadata": { "credits_charged": 12.942, "extra_credits": 7.942, "…": "…" },
"error": null
}| Field | Meaning |
|---|---|
rows, clustered, skipped | Rows returned, rows placed in a theme, and rows with no usable text (which are in no theme). |
named | true when a provider named the clusters; false when they carry the placeholder names. |
themes[].theme | The cluster's name. |
themes[].size | Rows in the cluster. |
themes[].row_ids | The rows' ids (the record's own id, or row-<index>). On multi-platform search they are platform:id. |
themes[].sample | A representative text from the cluster. |
themes[].terms | The words that define the cluster. These come from the clustering, not from a model. |
clustering | The clustering method, cluster/1. |
methodology_version, status, extra_credits | As on Labels. |
How to read it honestly#
- Themes are a reading aid, not a taxonomy. Grouping is by shared words. It works best on short comments; it can merge long texts that share vocabulary, and it can split paraphrases that share no words.
- A name is a summary of a sample, not a finding. Check
termsandsamplebefore you quote a theme's name. - Every row is accounted for.
skippedrows are in no theme, soclusteredplusskippedisrows. - Not deduplicated across platforms. On multi-platform search, the same URL posted on two platforms is two rows, and clustering runs once over the kept rows of every platform that accepts enrichment.
Multi-platform search#
On /v1/search/multi, data.themes is computed once over the kept rows of every opted-in platform (after relevance=filter, if used), and a stream carries it in an enrich frame. See Streaming.
What it costs#
One separate charge for the naming call, on top of the endpoint's price. A call with the provider off returns the placeholder-named clusters at no extra charge. See AI pricing.
Errors it can return#
| Status | Type | When |
|---|---|---|
| 422 | INVALID_PARAMS | themes on an endpoint that does not accept enrichment. Free. |
| 429 | AI_BUDGET_EXCEEDED | Today's allowance is used up and strict=true. |
| 503 | AI_UNAVAILABLE | Enrichment is off or unavailable and strict=true. |
| 402 | INSUFFICIENT_CREDITS, KEY_BUDGET_EXCEEDED | The reservation cannot be covered. |
See Errors for the full list.