Engagement-weighted Share of Voice across 2-5 brands, with web+social split, emotion overlay, and ESOV.
Per brand, fans out to content_analysis summary/phrase-trends/sentiment plus /search/everywhere (consuming the real per-platform items_by_source counts, not the capped fused list), then computes a published-formula SOV (web + social, equal-weighted by default), optional true-share-of-category (numeric category_code), and ESOV (market_shares, Binet/Field: a planning signal, not a growth guarantee). The web share is divided from the windowed phrase-trends sum over date_from..date_to, never from the upstream's all-time total_count, which is censored at 4,000,000 and would tie any two brands above that cap at exactly 50/50; the all-time figure still rides along per brand as web_mentions_all_time with a web_mentions_all_time_censored flag. One axis is chosen for the whole table and named in methodology.web_axis, so a brand whose timeline is unavailable can never be compared against a rival's on a different footing; if no brand's timeline is available the table falls back to the all-time totals and _warnings states that your window was not applied, and if those totals are censored it returns no web share at all instead of a fabricated tie. Every measured brand is scored on the SAME evidence basis: if one brand's social leg returns nothing, the whole table drops to the axes every brand has, methodology.basis names it, and _warnings says which brand caused it, so the column is always a real share rather than a mix of formulas. methodology ships the weights, the formula, the axis and the denominators, so you can recompute any share by hand from the same payload. With the social leg on, each brand's highest-engagement social rows (up to methodology.about_brand.max_rows_per_brand) are also judged for whether they are about that brand at all, and a second set of numbers is published next to the first, never in place of it: engagement_sum_about_brand, social_sov_pct_about_brand and sov_pct_about_brand leave out the rows under methodology.about_brand.about_brand_at_or_above, and per_brand[].about_brand lists the excluded row ids, their engagement, and the negative mentions by aspect. The web axis is an index count and is not cleaned. If any brand's rows could not be judged the cleaned numbers are null for the whole table and _warnings says about_brand_unavailable. The price is the same either way. Metered at 40cr/brand (20 web-only); a brand whose social search half-failed is automatically cheaper, with the reason legible in legs[] + per-brand coverage.
Request
- Base URL: https://www.socialcrawl.dev/v1
- URL: https://www.socialcrawl.dev/v1/prism/share-of-voice
- Auth: API key in header x-api-key
Query parameters
2-5 competitor brand names (CSV).
Numeric category-taxonomy code for true-share-of-category (look one up with GET /v1/content_analysis/categories).
JSON map of real market share per brand (fraction or %), e.g. {"notion":0.4,"coda":0.25,"airtable":0.35}, to compute ESOV.
CSV toggles (default emotions,social). Drop social for the cheaper web-only variant; drop emotions to skip the sentiment leg. The emotion overlay is third-party NLP passed through unmodified and is not scoped to your date window; methodology.sentiment_provenance states that on every response that carries it.
Optional surface filter forwarded to the content_analysis legs.
Window start (YYYY-MM-DD). Defaults to 90 days ago.
Window end (YYYY-MM-DD). Defaults to today.
Optional JSON map of a one-line description per brand, e.g. {"notion":"the workspace app"}, used to tell a brand apart from its namesakes.
Headers
Send no-cache to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the no-cache directive triggers this. See the Response Schema guide for details.
Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives 409 IDEMPOTENCY_IN_PROGRESS with a Retry-After of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: credits_used becomes 0, idempotent_replay becomes true, and credits_remaining is refreshed to the current balance. A known current balance appears in both the body and X-Credits-Remaining header; no balance row resolves to 0. On a transient lookup failure, body credits_remaining is null and X-Credits-Remaining is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use Accept: application/json when sending this header. The unsupported key-plus-SSE combination is rejected before billing.
Response
Successful response
Example response
{
"success": true,
"platform": "prism",
"endpoint": "/v1/prism/share-of-voice",
"credits_used": 1,
"credits_remaining": 4999,
"request_id": "req-abc123",
"idempotent_replay": true,
"meta": {
"hint": {
"code": "search_multi",
"message": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged.",
"method": "GET",
"path": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
}
}
}Changes
No recorded changes to this operation across all 1 revision of this API.