Brand mentions report
GET https://api.onekey.dev/v1/reports/brand-mentions?brand=... searches for a brand on several platforms, merges the answers by rank, and counts. You get how many mentions were found and where, who posted the most engaging ones, the top mentions, and, if you ask for it, counts of sentiment, complaints, questions and themes. See Reports for the shape and rules every report shares.
curl "https://api.onekey.dev/v1/reports/brand-mentions?brand=rust&platforms=hackernews,devto&top=3" \
-H "x-api-key: YOUR_API_KEY"Inputs#
| Parameter | Values | What it does |
|---|---|---|
brand | text, required, up to 200 characters | The brand or product name to search for. |
platforms | comma list | Search exactly these. Each needs a search endpoint. Default: the pool, which is Bluesky, DEV Community, Hacker News, Lemmy, Mastodon, Reddit, Stack Exchange, TikTok, Twitch, X (Twitter), YouTube. |
max_platforms | 1 to 12, default 6 | Cap on the platform list. |
top | 1 to 50, default 10 | How many top_authors and top_mentions to return. |
include=labels, label= | presets | Judge every row. Default presets sentiment, complaint, question. See Labels. AI. |
relevance, threshold | score or filter; 0 to 1 | Judge each row against the brand name. AI. |
themes | true | Cluster the rows and name the clusters. AI. |
strict | true (default) or false | What happens when nothing could be judged. |
dry_run | true | See Dry run. |
An unknown parameter is refused with INVALID_PARAMS.
The response#
Without any AI parameter, the report has these fields on top of the shared shape:
| Field | Meaning |
|---|---|
stats.mentions | Items over the legs that succeeded. |
stats.volume_per_platform | Items per platform. null for a platform whose leg failed, never 0. |
top_authors | Authors per platform, ranked (below). Each has platform, author, mentions, engagement_rate, interactions and ranked_by. |
top_mentions | The first top entries of the rank-fused list, each with score, platform, platforms and the item itself. |
fuse | {method: "rrf", k: 60}: reciprocal rank fusion. |
How top authors are ranked#
ranked_by tells you which basis was used, and the tiers come in this order: authors whose items carry a divisor are ranked by computed.engagement_rate (the published formula); then by raw interactions (likes plus comments plus shares); then by mention count.
Expect interactions. engagement_rate needs views or a follower count on the item, and most search rows carry neither. On those rows engagement_rate is null and the report falls back to raw interactions, saying so in ranked_by. That is a different scale: interactions are counts, not a rate, and are not comparable across platforms with different audience sizes.
The AI part#
Ask with include=labels, themes=true or relevance= and the report adds counts:
| Field | Meaning |
|---|---|
stats.sentiment | Counts by level: positive (levels 4 and 5), neutral (3), negative (1 and 2), plus unjudged and judged. null unless labels were requested. |
stats.complaints, stats.questions | Rows at or above a probability of 0.5 for that preset, with rows, judged, unjudged, count and share_of_judged. |
stats.top_themes | The largest clusters of the themes block. |
labels, themes, relevance | The same blocks as on multi-platform search, with their methodology_version. |
Platforms whose search endpoint does not accept labels are counted in the volume but never judged (labels.unsupported lists them).
A recorded call#
Recorded on the dev API with the pre-release test provider, heavily shortened:
curl "https://api.onekey.dev/v1/reports/brand-mentions?brand=rust&platforms=hackernews,devto&include=labels&themes=true&top=3" \
-H "x-api-key: YOUR_API_KEY"{
"success": true,
"data": {
"report": "brand_mentions",
"methodology_version": "brand_mentions/1",
"inputs": {
"brand": "rust", "platforms": ["hackernews", "devto"], "selection": "explicit", "top": 3,
"subqueries": { "hackernews": "rust", "devto": "rust" }, "adapted": {}, "skipped": {},
"labels": ["complaint", "question", "sentiment"], "themes": true, "relevance": null
},
"legs": ["..."],
"coverage": { "answered": ["hackernews", "devto"], "failed": [], "missing_fields": {} },
"stats": {
"mentions": 20,
"volume_per_platform": { "hackernews": 10, "devto": 10 },
"sentiment": { "positive": 0, "neutral": 20, "negative": 0, "unjudged": 0, "judged": 20 },
"complaints": { "preset": "complaint", "threshold": 0.5, "rows": 20, "judged": 20, "unjudged": 0, "count": 1, "share_of_judged": 0.05 },
"questions": { "preset": "question", "threshold": 0.5, "rows": 20, "judged": 20, "unjudged": 0, "count": 3, "share_of_judged": 0.15 },
"top_themes": [ { "theme": "Rust Switching Bot", "size": 5, "terms": ["rust", "switching", "bot", "boxr", "card"], "sample": "Why Discord is switching from Go to Rust" } ]
},
"top_authors": [
{ "platform": "hackernews", "author": "formalsystem", "mentions": 2, "engagement_rate": null, "interactions": 3136, "ranked_by": "interactions" }
],
"top_mentions": [
{
"platform": "hackernews", "platforms": ["hackernews"], "score": 0.016393,
"item": {
"content": { "id": "22238335", "title": "Why Discord is switching from Go to Rust" },
"author": { "username": "Sikul" },
"engagement": { "likes": 1582, "comments": 642 },
"computed": { "engagement_rate": null, "language": "en", "labels": { "complaint": { "p": 0.05 }, "question": { "p": 0.7 }, "sentiment": { "level": 3, "score_0_1": 0.5, "confidence": 0.35 } } }
}
}
],
"fuse": { "method": "rrf", "k": 60 },
"labels": { "status": "complete", "presets": ["complaint", "question", "sentiment"], "rows": 20, "labelled": 20, "extra_credits": 27.915, "methodology_version": "fake/1" },
"themes": { "status": "complete", "rows": 20, "named": true, "extra_credits": 6.211, "methodology_version": "fake/1" },
"ai_methodology_version": "fake/1",
"credits": { "legs": 5.0, "ai": 34.126, "total": 39.126 }
},
"metadata": { "recipe": "brand_mentions", "report": "brand_mentions", "methodology_version": "brand_mentions/1", "credits_charged": 39.126, "extra_credits": 34.126, "pipeline": ["..."] }
}How to read it: all 20 rows are neutral because the test provider's sentiment on plain titles is level 3 by construction, and engagement_rate is null on every author, so ranked_by is interactions. The counts show that the pipeline works end to end; they are not findings about Rust.
Costs#
Measured on the pre-release test provider (synthetic usage, so it shows how the charge scales, not what production judgments will cost): 5 credits for the legs (Hacker News was a cache hit from an earlier call), 27.915 for judging 20 rows with 3 presets (1.40 per row), 6.211 for naming 7 clusters, 39.126 in all. Fewer presets are cheaper. The legs alone, without any AI parameter, cost only the search endpoints' prices. See AI pricing for how metering works.
When AI is not enabled#
Without include=labels, stats.sentiment is null, and the rest of the report works. With labels requested and the default strict=true, the call is a free AI_UNAVAILABLE before any fetch. With strict=false you get stats.sentiment as {positive: 0, neutral: 0, negative: 0, unjudged: 20, judged: 0} and labels.reason: "provider_off", at the legs' price.
Limits#
- Each platform's search decides what counts as a mention. The report counts what the search returned, the first page of each, not everything ever written.
- A tag-style search receives one keyword.
inputs.adaptedshows it. top_authorsfalls back to raw interactions on most searches, as above.- AI-derived counts are plumbing checks until a production method exists.
- Reports are JSON only. A
Accept: text/event-streamheader is ignored, and a report is not yet a target for a monitor.