# More reports

Eighteen more routes under `https://api.onekey.dev/v1/reports/`, in the same style as [brand mentions](https://onekey.dev/docs/reports/brand-mentions.md), [creator vet](https://onekey.dev/docs/reports/creator-vet.md) and [demand signals](https://onekey.dev/docs/reports/demand-signals.md). Read the [shared rules](https://onekey.dev/docs/reports.md#the-shape-every-report-shares) first: the response shape, `coverage`, honest nulls, `dry_run`, billing and idempotency are exactly those.

What is different here:

- **No AI step.** Nothing on this page is judged by a model. Every figure is a platform field or arithmetic that is written out below, and `methodology_version` (for example `crisis_radar/1`) changes if the arithmetic does. `ai_methodology_version` is always `null` and `credits.ai` is `0`.
- **Built from endpoints that exist.** Each report names the endpoints it calls (its `legs`). A report that would need data the catalogue does not have is not offered; see [what is not built](#what-is-not-built).
- **Samples, not totals.** Search legs return the first page of a platform's search. Where a figure is computed over those pages the report says so in `notes` or `sample`; it never presents a page as a total.
- **Priced as its legs.** The price is the sum of the legs that succeeded, no surcharge. A leg that fails is refunded as usual.
- **Dependent legs.** A few reports run a second round whose inputs come from the first (an org's top repos, a Truth Social account id). The second round is capped, billed leg by leg, skipped (and said so in `coverage.dependent_legs`) if the balance no longer covers it, and priced at its cap by `dry_run=true`.

## At a glance

Prices are the list prices of the default legs in credits (cache hits are free; `dry_run=true` shows the legs and the worst case for 0 credits). A report with a platform pool takes `platforms` and `max_platforms` like [the first three](https://onekey.dev/docs/reports.md#choosing-platforms).

| Route | Input | Default legs | List price |
| --- | --- | --- | --- |
| [`crisis-radar`](#crisis-radar) | `brand` | news volume and tone timelines | 10 |
| [`crisis-postmortem`](#crisis-postmortem) | `brand` | up to 4 search platforms + news articles | about 17 |
| [`share-of-voice`](#share-of-voice) | `brands` (2 to 5) | per brand: news volume + 2 search platforms | about 14 for 2 brands |
| [`reputation`](#reputation) | `brand` | App Store search + news tone | 10 |
| [`campaign`](#campaign) | `query`, `start`, `end` | news volume + up to 3 search platforms | about 12 |
| [`devtool-pulse`](#devtool-pulse) | `query` | repo search, Hacker News, Reddit, dev.to, Stack Exchange | 19 (a named `repo`: 15) |
| [`launch-echo`](#launch-echo) | `query` | Hacker News threads and comments, dev.to | 11 |
| [`org-radar`](#org-radar) | `org` | org + repos listing, then dossier and top issue for the top 3 | 12 |
| [`leads`](#leads) | `competitor` | up to 4 search platforms | about 12 |
| [`audience-questions`](#audience-questions) | `topic` | up to 4 search platforms | about 12 |
| [`find-accounts`](#find-accounts) | `name` | one user-search endpoint | 3 per platform |
| [`brief-check`](#brief-check) | `url`, `points` | the post's own endpoint | 2 to 3 |
| [`format-lift`](#format-lift) | `platform`, `handle` | the platform's recent-posts feed | 1 to 5 |
| [`trend-board`](#trend-board) | `country` | five popular lists | 17 |
| [`apps-lookup`](#apps-lookup) | `title` | App Store search + Google Play search | 8 |
| [`truthsocial-pulse`](#truthsocial-pulse) | `handle` | profile + news search, then recent posts | 6 |
| [`voice`](#voice) | `handle` | recent posts on up to four platforms | about 11 |
| [`handle-audit`](#handle-audit) | `handle` | profile and posts feed on 3 platforms | about 9 |

## crisis-radar

`GET /v1/reports/crisis-radar?brand=acme`. Is news attention to a brand unusually high today, and has its tone dropped? Legs: [`gdelt.volume_timeline`](https://onekey.dev/docs/api/gdelt/volume_timeline.md) and [`gdelt.tone_timeline`](https://onekey.dev/docs/api/gdelt/tone_timeline.md).

Both timelines are averaged per UTC day. For each, `z = (newest day - mean of the baseline days) / sample standard deviation`, where the baseline is the `baseline_days` days before the newest (7 to 90, default 28). With fewer than 7 baseline days, or a flat baseline, `z` is `null` with a `reason`. A volume spike (`z` up) or a tone drop (`z` down) at or above `threshold` (default 2) is a breach. The `severity.grade` steps up for every full standard deviation beyond the threshold: `elevated`, `high`, `severe` (`none` below it, `unknown` with no figure).

```json
{"volume": {"series_days": 29, "latest_day": "2026-09-30", "latest": 12.0, "baseline_days": 28, "baseline_mean": 1.5, "baseline_sd": 0.51, "z": 20.6},
 "tone": {"latest": -6.0, "z": -9.8},
 "severity": {"threshold": 2.0, "breach": {"volume": true, "tone": true}, "any_breach": true, "grade": "severe", "worst_signal_z": 20.6}}
```

Limits: the newest day may be partial. Tone is GDELT's average tone, not a share of negative posts, and there is no confirmation step: a breach means "unusual against its own history", not "a crisis".

## crisis-postmortem

`GET /v1/reports/crisis-postmortem?brand=acme&top=25`. Who said it first, where did it spread, and when did it peak. Legs: the search endpoint of up to 4 platforms (the [brand mentions](https://onekey.dev/docs/reports/brand-mentions.md) pool) plus [`gdelt.articles`](https://onekey.dev/docs/api/gdelt/articles.md).

All dated items are merged into a timeline (oldest first, `top` entries). `origin` is the earliest item, `peak` the UTC day with the most items (ties: the earlier day), `propagation` the platforms in the order of their first item with `lag_hours` from the origin, `daily_counts` the items per day. `sample` counts the items, how many were dated, and per platform.

Limits: each source is one page of its search, so `origin` is the earliest item in that sample, not a proven first mention, and the report says so in `sample.note`. There is no narrative text: nothing is generated.

## share-of-voice

`GET /v1/reports/share-of-voice?brands=acme,globex,initech`. Two to five brands, compared. Per brand: [`gdelt.volume_timeline`](https://onekey.dev/docs/api/gdelt/volume_timeline.md) plus a search leg on each chosen platform (default 2).

- **News share.** Each brand's mean GDELT volume intensity over the sum of the brands' means. Intensity is each query's share of all news coverage, so the ratio is comparable between brands.
- **Social share.** A brand's interactions (likes + comments + shares) over all brands' interactions, across the top results of each platform's search. It is a share of the **top results**, not of all posts, and it is page-size limited.

`leader_news` and `leader_social` name the larger share. No blended score, no emotion overlay and no excess-share-of-voice figure is computed. Cost grows with the number of brands: every brand gets its own legs.

## reputation

`GET /v1/reports/reputation?brand=acme` (optional `app_store_id`, `country`). Two sources the catalogue can supply: the App Store listing ([`itunes.search_apps`](https://onekey.dev/docs/api/itunes/search_apps.md), matched to the brand by normalised title, or [`itunes.lookup_app`](https://onekey.dev/docs/api/itunes/lookup_app.md) when you give `app_store_id`) with its `rating`, `rating_count`, price and category; and the news tone summary from [`gdelt.tone_timeline`](https://onekey.dev/docs/api/gdelt/tone_timeline.md) (mean, last 7 days against the 7 before).

`unavailable` lists what is not there and why: Trustpilot ratings (the reviews endpoint returns links only), Google Play ratings (no rating field) and Google Business. `blended_score` is always `null`: the sources do not share a scale, and nothing is blended.

## campaign

`GET /v1/reports/campaign?query=%23acme&start=2026-09-10&end=2026-09-20`. A hashtag or phrase around a window. `pre_days` (default the campaign length, at most 30) and `post_days` set the windows either side. Legs: [`gdelt.volume_timeline`](https://onekey.dev/docs/api/gdelt/volume_timeline.md) (the leading # is dropped for it) and up to 3 search platforms.

- **news_volume.** Mean daily GDELT intensity before, during and after, `lift_during_vs_pre_pct`, `lift_post_vs_pre_pct` and `lift_post_vs_during_pct`. A window the timeline does not cover is `null` with a warning (the timeline covers a few months back).
- **top_amplifiers** and **engagement_by_platform.** From the dated search rows inside the window: authors ranked by interactions, and rows and interactions per platform. Rows without a date or outside the window are counted in `sample`, not used.

Limits: only the news timeline measures volume. Social rows are the first page of each search, so social volume is not lifted.

## devtool-pulse

`GET /v1/reports/devtool-pulse?query=acme` (optional `repo=owner/name`). Developer-brand health in one call. Legs: [`github.search_repos`](https://onekey.dev/docs/api/github/search_repos.md) (its top hit) or [`github.repo_dossier`](https://onekey.dev/docs/api/github/repo_dossier.md) when you name the repo, then Hacker News, Reddit, dev.to and Stack Exchange searches. `repo` has stars, forks, open issues, language, licence and last push; `mentions` has, per source, the rows, their interactions, the newest date and the `top` items. Each source is one page of its search.

## launch-echo

`GET /v1/reports/launch-echo?query=acme` (optional `repo`). How a launch landed: Hacker News threads ([`hackernews.search`](https://onekey.dev/docs/api/hackernews/search.md)) and comments ([`hackernews.search_comments`](https://onekey.dev/docs/api/hackernews/search_comments.md)), the dev.to echo ([`devto.search`](https://onekey.dev/docs/api/devto/search.md)) and optionally the repo dossier. Points and comment counts are the platform's own.

## org-radar

`GET /v1/reports/org-radar?org=acme&top_repos=3`. A GitHub org's footprint. First round: [`github.org`](https://onekey.dev/docs/api/github/org.md) and [`github.org_repos`](https://onekey.dev/docs/api/github/org_repos.md). Second round (dependent): for the top `top_repos` (1 to 5) by stars, archived repos skipped, [`github.repo_dossier`](https://onekey.dev/docs/api/github/repo_dossier.md) and [`github.top_issues`](https://onekey.dev/docs/api/github/top_issues.md) (the most discussed issue, as the top request).

`rollup` sums stars, forks and open issues over the first page of the repos listing, counts languages and archived repos and gives the last push. It is a rollup of that page, not necessarily of every repo; `notes` says so.

## leads

`GET /v1/reports/leads?competitor=Acme`. Posts where people look for an alternative to, or are leaving, a named product. The search runs "`Acme alternative`" on up to 4 platforms, then keeps the rows whose text matches one of these phrase rules (case-insensitive, the name matched literally):

| `matched` | Phrases |
| --- | --- |
| `alternative` | "alternative to / for / of / like Acme", "Acme alternative(s)" |
| `switching_from` | switch / move / migrate / leave / left, then "from / off / away from Acme" |
| `replacing` | replace / ditch / drop / cancel / quit, then "Acme" |
| `instead_of` | "instead of Acme" |
| `better_than` | better / cheaper / simpler than Acme |
| `looking_for` | "looking for / recommend / suggest / need ... like / than / over Acme" |

`leads` is ranked by interactions; `stats` counts the rows scanned, the leads and the matches per rule. A lead is a public post that matches a phrase: it says nothing about intent beyond the words.

## audience-questions

`GET /v1/reports/audience-questions?topic=keyboards`. The questions people ask about a topic. Search legs on up to 4 community, social and news platforms. A row is a question when its title or text has a "?" in its first 300 characters, or starts with an interrogative or auxiliary verb and has at least 4 words. Its **intent** is `vs` (it names two options with "vs" or "versus"), else its leading wh- word (`who`, `what`, `why`, `how`, `which`, `where`, `when`), `yes_no` for an auxiliary opener (can, should, is, do ...) or `other`. `clusters` are sorted by size, each with the `top` examples by interactions.

Limits: questions are found in search-result titles and text, not in comment threads.

## find-accounts

`GET /v1/reports/find-accounts?name=Ada%20Lovelace` (optional `platforms`, `top`). Candidate accounts for a person's or company's name on the user-search endpoints of Instagram, TikTok, Threads, Truth Social, Mastodon and X. By default only the cheapest platform is searched (2 credits); name more with `platforms=` and each costs its own price.

Each candidate has a `match` level from the name against the username and display name only: `exact` (the normalised name equals either), `strong` (every name token is in them), `possible` (at least half), `weak`. Candidates are ranked by level, then followers. They are for human review: nothing links accounts to a person.

## brief-check

`GET /v1/reports/brief-check?url=https://www.tiktok.com/@ada/video/123&points=acme x1|battery&forbidden=cure`. One post against a brief. The URL is resolved exactly as [`GET /v1/lookup`](https://onekey.dev/docs/lookup.md) does (an unknown URL is a free [`UNSUPPORTED_URL`](https://onekey.dev/docs/errors.md#unsupported-url)); the post is fetched once at that endpoint's price. `points` and `forbidden` are comma or `|` separated (use `|` when a phrase has a comma). `disclosure` defaults to `#ad`, `#sponsored`, `#paidpartnership`, `paid partnership`, `#gifted`, `#partner`, `sponsored`, `advertisement`, `#advertisement`.

Matching is literal, case-insensitive and whole-word on the caption text; a `#hashtag` matches only itself. `passes` is true when every point is found, no forbidden term is found and a disclosure term is present. Nothing in a video or image is read, and it is not legal advice.

## format-lift

`GET /v1/reports/format-lift?platform=bluesky&handle=ada`. Which structural hooks of a creator's own posts did better than the rest of their posts. Platforms with a recent-posts feed: Bluesky, dev.to, Hacker News, Instagram, Lobsters, Reddit, Threads, Twitch, X. One page of recent posts.

Hooks (each a plain text test): `question` ("?" in the first 140 characters), `number` (a digit in the first 8 words), `how_to`, `list` (starts with a number or "top N"), `emoji_open`, `hashtags_3plus`, `has_link`, `short_caption` (under 80 characters), `long_caption` (over 300). The metric is the mean `engagement_rate` when at least half the posts have one, else the mean interactions (`metric` says which). `lift_pct` = (mean with the hook / mean without - 1) x 100, only when at least `min_n` (default 3) posts are on each side; otherwise `null` with a `reason`. It describes this creator's own recent posts and is not a prediction.

## trend-board

`GET /v1/reports/trend-board?country=US` (optional `sources`, `top`). What is rising, with no keyword in: Google Trends ([`google_trends.trending`](https://onekey.dev/docs/api/google_trends/trending.md)), TikTok popular hashtags and videos for the country, Bluesky trending topics and Truth Social trending tags (those two are global and ignore the country; `scope` says which). One list per source in its own order: no fusion, no cross-source score.

## apps-lookup

`GET /v1/reports/apps-lookup?title=Acme` (optional `country`). One app title on both stores: [`itunes.search_apps`](https://onekey.dev/docs/api/itunes/search_apps.md) and [`googleplay.search`](https://onekey.dev/docs/api/googleplay/search.md). Each store's match is the result whose title equals the query after normalising (`exact`), else the first containing all its words (`contains`). The App Store match carries rating, rating count, price and category. Google Play search returns a name and a link only, so `comparison` is `null`: there is no cross-store rating comparison.

## truthsocial-pulse

`GET /v1/reports/truthsocial-pulse?handle=ada` (optional `news_query`, `top`). First round: [`truthsocial.profile`](https://onekey.dev/docs/api/truthsocial/profile.md) and a news search for the handle ([`google.news`](https://onekey.dev/docs/api/google/news.md)). Second round (dependent): [`truthsocial.user_posts`](https://onekey.dev/docs/api/truthsocial/user_posts.md) with the account id the profile returned. `posts` has the cadence (the same arithmetic as [creator vet](https://onekey.dev/docs/reports/creator-vet.md#cadence)), mean engagement and the top posts; `news_echo` is the news results, which are not proof the news is about this account.

## voice

`GET /v1/reports/voice?handle=ada` (optional `platforms`: any of `bluesky`, `threads`, `x`, `truthsocial`; `limit` up to 100). One handle's recent posts on those platforms, merged newest first. The same handle string is tried everywhere: an account that does not exist on a platform is a failed leg, never a guess. Truth Social adds a dependent round (profile, then posts).

## handle-audit

`GET /v1/reports/handle-audit?handle=ada` (`platforms` or `max_platforms`, default 3). The same handle across profile platforms: whether it exists, followers, the date of its last post and posts per week, and what pulling its posts costs: the feed endpoint's price per page, the page size observed and `credits_per_100_items`. `ranking` orders the platforms where the handle exists by followers, then by the newest post. No quality or authenticity score is computed.

## What is not built

Some of the same questions cannot be answered honestly from the catalogue today. They are not offered rather than faked:

| Question | Why not |
| --- | --- |
| AI visibility and multi-engine answers | Need paid AI-engine access that is not connected. |
| Agent-style investigation | Needs a language-model planner; nothing here is model-driven. |
| Screening a person for adverse posts | Needs judgment of a private person's posts; keyword lists would be misleading. |
| Buyer leads from comment threads | Needs search, then comments per post, then an intent judgment. [`leads`](#leads) covers the posts themselves. |
| How early a term appeared | No source sorts a platform's search oldest first; a first page is not the earliest. |
| Global versus Korean conversation gap | Needs a Naver key and cross-language term mapping. |
| Audience overlap and commenter language | Needs a profile, posts and comments fan-out per creator; not built yet. |
| Review integrity | The catalogue's review endpoints return links, not ratings or dates. |

## Errors

Bad input is refused before anything runs, free: [`INVALID_PARAMS`](https://onekey.dev/docs/errors.md#invalid-params) (a missing or malformed parameter, an unknown one), [`UNSUPPORTED_PLATFORM`](https://onekey.dev/docs/errors.md#unsupported-platform), [`NO_SEARCHABLE_PLATFORMS`](https://onekey.dev/docs/errors.md#no-searchable-platforms), [`UNSUPPORTED_URL`](https://onekey.dev/docs/errors.md#unsupported-url) (brief-check). An all-legs-failed report fails with the first leg's error and charges nothing; a partial answer is a `200` whose `coverage` lists the failed legs.

## Related

- [Reports](https://onekey.dev/docs/reports.md): the shared rules.
- [Universal search](https://onekey.dev/docs/universal-search.md), [Multi-platform search](https://onekey.dev/docs/search-multi.md), [Batch](https://onekey.dev/docs/batch.md).
- [Monitors](https://onekey.dev/docs/monitors.md) to be told when numbers move.
