Analyses

An analysis is Siftsy’s answer to “what did the audience say about this content?” It is always computed over a content selection, it always has two sides, and it always has comments underneath it.

What an analysis is

Every analysis pairs the creator side with the audience side:

SideKeyWhat it holds
Content (creator-side)contentWhat was posted: the brief, the creator topics and messages Siftsy scored against, and content / topic scores and mix
Comment analysis (audience-side)commentAnalysisHow commenters responded: Siftsy score, sentiment, relevance, consensus, content, topic on 1–10, plus five-bucket mix bars for each

The relationship between the two is the analysis. A post whose content scores well but whose comment analysis is skeptical is a message that did not land; a post with hostile topic sentiment but agreeing consensus is an audience united against the subject, not against the creator. Comment bodies are deliberately not on the analysis — they live one level down at comments.path as a sample of evidence, so an analysis stays small and you are not exporting the thread.

Consensus is not agreement with the post. Sentiment, content, and topic describe how commenters feel about the post, creator, or subject. Consensus describes how they react to each other. See Scores.

Kinds of analysis

KindHow you get itOverComputed
Post analysisGET /posts/{postId}/analysisOne postMaterialized when the post was scored; never recomputed. See Post analysis
SnapshotPOST /analyses without searchA selection (≤ 1,000 posts)Inline from stored comment scores; 201 ready for ≤ 100 posts
SearchPOST /analyses with search in mode: ai (the default)A selectionAs a job: the AI search runs over every selected comment, then the same math as a snapshot. mode: keyword is deterministic and stays a synchronous snapshot
ReportGET /analyses?kind=reportA campaignSaved in Siftsy by a teammate; the API exposes the stored write-up

Snapshots, searches, and reports all share the /analyses routes and the same comments.path pattern. kind and status on each row tell you which you are looking at.

Narrowing an analysis

An analysis over all comments in a selection is the baseline. Most questions are about a subset, and there are two ways to name one:

  • Filters are structural — a comment either matches or it does not. Mix-bar buckets (sentiment: ["negative"]), keywords, usernames, creator comments, replies, questions, mentions, emoji-only, a likes floor, a date range. They are the dashboard analysis-view controls, evaluated with the same code, so a filtered analysis here matches what a teammate sees when they click the same bars.
  • Search is semantic — “comments asking where to buy it” — and needs a model to decide what matches. mode: ai runs the dashboard’s search; mode: keyword is the deterministic fallback.

Either way the result is the same shape. The analysis numbers describe only the matched comments, and scope tells you how big that subset is relative to the whole selection:

"scope": { "totalComments": 4820, "matchedComments": 611, "shareOfComments": 0.1268, "matchedPosts": 11, "shareOfContent": 0.9167 }

Read those two shares together. A theme at 13% of comments spread across 92% of posts is a broad, low-intensity current; the same 13% concentrated on 2 of 12 posts is a flashpoint.

Why some analyses are jobs

Snapshots aggregate scores Siftsy already stored — no model runs, and up to 100 posts they finish inside the request. AI search has to read every selected comment through a language model, and very large selections have to be loaded and filtered before any math happens. Those cannot reliably finish in one HTTP round-trip, so they run in the background.

Rather than two APIs, there is one resource with a status: queuedrunningready | failed. A 201 means the analysis is already ready; a 202 means poll GET /analyses/{id} (honoring Retry-After) until it is. The payload is identical once ready, and both kinds persist for 7 days and serve GET /analyses/{id}/comments from the same stored definition. Details are in the Jobs section below.

The comments underneath

GET /analyses/{id}/comments returns a sample of comments behind the analysis — after its own filters and search matches have been applied — not the full matched set and not the raw thread. Pass source=notables (engagement + relevance), content (grouped onto creator topics/messages), or all (union). Mix-bar query filters narrow that sample further.

Siftsy is an analysis platform, not a comment exporter. Use this route for evidence. Do not use it to dump full comment sections.

See Retrieving comments.


Reference: POST /analyses

POST /analyses runs the selection → analysis → comments flow as an API call:

POST /analyses select content, narrow comments, get an analysis
GET /analyses/{analysisId} poll / read it
GET /analyses/{analysisId}/comments a sample of comments behind the analysis
GET /analyses everything you have created, plus saved reports

Requires the analyses:run scope to create, analyses:read to read, and comments:read for comments. Nothing here rescores posts or changes your workspace; analyses are computed from stored comment scores and expire after 7 days.

1. Select content

selection names the posts — see Content selection for the concept. Use exactly one style (a collection may additionally be narrowed with posts filters):

StyleBodyNotes
Collection{ "collectionId": "col_…" }Campaign ids include nested groups
Post filters{ "posts": { "account": "@acme", "tag": "launch", … } }Same filters as GET /posts, plus tag
Collection + filters{ "collectionId": "col_…", "posts": { "platform": "tiktok" } }Filters apply inside the collection
Explicit ids{ "postIds": ["p_1", "p_2"] }Up to 1,000; cannot be combined

posts.status defaults to complete. Selections resolve newest first and stop at 1,000 postsselectionTruncated: true tells you when more matched. A selection that matches nothing returns 400.

2. Filter the comments (optional)

filters are the dashboard analysis-view controls. Only comments matching all active filters count toward the analysis, and the comments sample is drawn from that matched set. Hidden comments are always excluded.

FilterValuesDashboard equivalent
sentiment, content, topicpositive, lean_positive, neutral, lean_negative, negativeMix-bar click
relevancebuckets above, or on_topic, relevant, general, off_topicRelevance bar
consensusbuckets above, or agreeing, discussing, debating, fightingConsensus bar
keywords, keywordMatchup to 50 terms; any (default) or allKeyword include
excludeKeywordsup to 50 termsKeyword exclude
usernames, excludeUsernamesup to 50 (leading @ optional)Username include / exclude
creatorinclude, exclude, onlyCreator comments
repliesinclude, exclude, onlyReplies
questionsinclude, exclude, onlyQuestions
mentionsinclude, exclude, only@-mentions
emojiinclude, exclude, onlyEmoji-only comments
minLikesnumber ≥ 0Likes floor
since, untilISO 8601Comment date range

Consensus is not agreement with the post. It measures how commenters react to each other. consensus: ["fighting"] returns comments arguing with other commenters, whatever they think of the post.

search looks for meaning inside the selected comments:

{ "search": { "query": "complaints about shipping or delivery", "mode": "ai", "minRelevance": 6 } }
  • mode: "ai" (default) is the dashboard’s semantic search. It always runs as a job.
  • mode: "keyword" is deterministic: the query is folded into filters.keywords and the analysis stays synchronous.

include adds optional extras computed from the matched comments: ["wordCloud", "emojiCloud"].

3. Read the analysis

The response is one Analysis resource. status is the only thing that changes between a snapshot and a job:

CaseResponsestatus
No search, ≤ 100 posts201 Createdreadyscope, content, commentAnalysis populated
AI search, or more than 100 posts202 Accepted + Location, Retry-Afterqueued, then running, then ready or failed

Poll GET /analyses/{analysisId} until the status is terminal. Non-terminal responses carry Retry-After (seconds) and meta.pollAfterMs; progress.phase and progress.percent describe where the job is. A failed analysis has error.message.

curl -sS -X POST -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
https://api.siftsy.app/analyst/v1/analyses \
-d '{
"selection": { "collectionId": "col_launch" },
"filters": { "sentiment": ["negative", "lean_negative"], "replies": "exclude" },
"include": ["wordCloud"]
}'
{
"data": {
"id": "an_5f2c9a1e0b6d4d0f9c1a2b3c4d5e6f70",
"kind": "snapshot",
"status": "ready",
"createdAt": "2026-09-10T15:04:05.000Z",
"completedAt": "2026-09-10T15:04:05.400Z",
"expiresAt": "2026-09-17T15:04:05.000Z",
"selection": { "type": "collection", "collectionId": "col_launch", "status": "complete" },
"filters": { "sentiment": ["negative", "lean_negative"], "replies": "exclude" },
"search": null,
"include": ["wordCloud"],
"postCount": 12,
"postIds": ["p_301", "p_298", "p_290"],
"selectionTruncated": false,
"scope": {
"postCount": 12,
"totalComments": 4820,
"matchedComments": 611,
"matchedPosts": 11,
"shareOfComments": 0.1268,
"shareOfContent": 0.9167
},
"content": {
"postCount": 12,
"posts": [{ "id": "p_301", "platform": "tiktok", "creator": { "username": "acme" }, "matchedComments": 97 }],
"postsTruncated": false,
"topics": [{ "title": "Product launch", "description": null, "postCount": 12 }],
"messages": [{ "topic": "Product launch", "stance": "The new line ships faster", "postCount": 8 }],
"scores": { "content": 7.1, "topic": 6.4 },
"mix": { "content": { "positivePercent": 41, "leanPositivePercent": 22, "neutralPercent": 18, "leanNegativePercent": 11, "negativePercent": 8 },
"topic": { "positivePercent": 35, "leanPositivePercent": 27, "neutralPercent": 20, "leanNegativePercent": 10, "negativePercent": 8 } }
},
"commentAnalysis": {
"briefSummary": null,
"scores": { "scoreVersion": 3, "siftsyScore": 3.4, "sentiment": 2.3, "relevance": 7.8, "consensus": 6.9, "content": 2.1, "topic": 2.6 },
"mix": {
"sentiment": { "positivePercent": 0, "leanPositivePercent": 0, "neutralPercent": 0, "leanNegativePercent": 38, "negativePercent": 62 },
"relevance": { "positivePercent": 58, "leanPositivePercent": 24, "neutralPercent": 9, "leanNegativePercent": 6, "negativePercent": 3 },
"consensus": { "positivePercent": 44, "leanPositivePercent": 31, "neutralPercent": 12, "leanNegativePercent": 9, "negativePercent": 4 },
"content": { "positivePercent": 0, "leanPositivePercent": 2, "neutralPercent": 5, "leanNegativePercent": 40, "negativePercent": 53 },
"topic": { "positivePercent": 1, "leanPositivePercent": 3, "neutralPercent": 6, "leanNegativePercent": 41, "negativePercent": 49 }
},
"topics": [],
"notableComments": [{ "postId": "p_301", "commentId": "c_77f1" }, { "postId": "p_298", "commentId": "c_1a09" }],
"outliers": [],
"wordCloud": [{ "text": "shipping", "value": 84, "type": "word", "avgSentiment": 2.1, "sentimentCategory": "negative", "mentionPercentage": 13.7 }],
"emojiCloud": null,
"totalComments": 611,
"completeness": "scores_and_content"
},
"comments": { "path": "/analyses/an_5f2c9a1e0b6d4d0f9c1a2b3c4d5e6f70/comments" },
"computedAt": "2026-09-10T15:04:05.400Z"
},
"meta": { "status": "ready" }
}

Payload, field by field

FieldWhat it is
id, kind, statusan_… id; snapshot or search; queued / running / ready / failed
createdAt, updatedAt, completedAt, expiresAtLifecycle timestamps; analyses expire 7 days after creation
selection, filters, search, includeNormalized echo of the request
postCount, postIds, selectionTruncatedWhat the selection resolved to (newest first)
progress, pollAfterMsOnly while queued / running
errorOnly when failed
scope.totalCommentsVisible comments across the selection before filters / search
scope.matchedComments, scope.matchedPostsComments (and the posts they sit on) that matched — the set every number below describes
scope.shareOfComments, scope.shareOfContentmatched / total as 0–1 fractions
content.posts[]Up to 100 post cards, each with matchedComments
content.topics[], content.messages[]Creator topics / messages across the selection, deduped with postCount
content.scores, content.mixContent and topic score / mix over the matched comments
commentAnalysis.scoresscoreVersion, Siftsy score, sentiment, relevance, consensus, content, topic (1–10) — the same math as the dashboard vibe check
commentAnalysis.mixFive-bucket mix bars for each dimension
commentAnalysis.notableComments[]{postId, commentId} pairs — highest-engagement, most relevant matched comments
commentAnalysis.wordCloud, emojiCloudPresent when requested via include
commentAnalysis.totalComments, completenessMatched count and how much of the panel is filled
commentAnalysis.briefSummary, topics, outliersReserved for generated analysis text (brief, themes, outliers); null / empty in this version
comments.pathSample of comments behind this analysis. Relative to the base URL (https://api.siftsy.app/analyst/v1 + path); the Location header on 201 / 202 is already a full host path (/analyst/v1/analyses/…)

4. Sample the comments

GET /analyses/{analysisId}/comments returns a sample of the comments the analysis describes — not the full matchedComments set and not the raw thread. The analysis’s own filters and search matches are applied first, then source=notables|content|all picks the evidence. Mix-bar query filters narrow that sample further. See Retrieving comments.

curl -sS -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.siftsy.app/analyst/v1/analyses/an_5f2c9a1e0b6d4d0f9c1a2b3c4d5e6f70/comments?source=notables&consensus=fighting"

Until the analysis is ready this route returns 409 with error.message of analysis_not_ready (honor Retry-After) or analysis_failed.

Jobs: when and why

Some analyses are instant and some are not, so the API treats them the same way and lets status tell you which you got.

  • Snapshots aggregate scores Siftsy already stored — no model runs. Up to 100 posts, they finish inside the request and come back 201 ready. There is nothing to poll.
  • Jobs are needed when the work is too slow or too variable for a single HTTP request: AI search runs the language model over every selected comment, and large selections (hundreds of posts, tens of thousands of comments) have to be loaded and filtered first. These return 202 queued immediately and finish in the background.

Because the resource is identical in both cases, a client can always follow the same loop:

POST /analyses
201 → use data now
202 → GET Location until status ∈ {ready, failed}, waiting Retry-After between polls
GET /analyses/{id}/comments?source=notables

Snapshots are not stored any less carefully than jobs — both persist for 7 days, appear in GET /analyses, and serve a comments sample from the same stored definition, so a dashboard that fetched an analysis once can keep sampling evidence without recomputing.

Worked example: Dove and Dove influencers

A Dove workspace has two collections: Dove Brand Latest — five TikTok posts from the official @dove account (holiday scrubs, a Dove x Crumbl collab at Walmart, scent layering, deodorant ASMR) — and Dove Influencers, the creator posts made for the same push. The walk-through below runs the full selection → analysis → comments loop against them. Brand-side figures come from Siftsy’s sample workspace; influencer-side figures are illustrative.

1

Find the selections

curl -sS -H "Authorization: Bearer $SIFTSY_API_KEY" \
"https://api.siftsy.app/analyst/v1/collections"
{
"data": [
{ "id": "col_dove_brand", "name": "Dove Brand Latest", "type": "campaign", "parentId": null, "postCount": 5 },
{ "id": "col_dove_influencers", "name": "Dove Influencers", "type": "campaign", "parentId": null, "postCount": 14 }
]
}

The brand collection is the same thing as a post filter on the account — either selection works:

curl -sS -H "Authorization: Bearer $SIFTSY_API_KEY" \
"https://api.siftsy.app/analyst/v1/posts?account=@dove&platform=tiktok"
{
"data": [
{ "id": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "platform": "tiktok", "link": "https://www.tiktok.com/@dove/video/7583010641535831351/",
"creator": { "username": "dove", "name": "Dove" }, "title": "The chaotic extrovert vs. the patient introvert #ScrubTok",
"engagement": { "views": 39300, "likes": 1077, "comments": 70, "shares": 21 }, "totalComments": 47,
"scores": { "scoreVersion": 2, "siftsyScore": 7.1, "sentiment": 7.1 } },
{ "id": "845b7346-8b22-43db-99a1-213b64f2a256", "link": "https://www.tiktok.com/@dove/video/7582618479073824013/",
"title": "Coconut + vanilla scent layering", "engagement": { "views": 9911, "likes": 521, "comments": 22, "shares": 17 }, "totalComments": 21 },
{ "id": "708024ec-1cb8-4f28-9b3f-b0a0964a9029", "link": "https://www.tiktok.com/@dove/video/7584105296365178167/",
"title": "Deodorant ASMR — 72-hour protection", "engagement": { "views": 6663, "likes": 227, "comments": 29, "shares": 6 }, "totalComments": 28 },
{ "id": "b90d199b-178d-4471-8cf5-69df667fe7a8", "link": "https://www.tiktok.com/@dove/video/7584858772028280119/",
"title": "Guess the scrub: Holiday Treats Edition", "engagement": { "views": 6031, "likes": 324, "comments": 23, "shares": 7 }, "totalComments": 23 },
{ "id": "948569f6-9e06-4d2a-b0ba-1fec81c744b7", "link": "https://www.tiktok.com/@dove/video/7585235900200750366/",
"title": "Dove x Crumbl Tres Leches at Walmart", "engagement": { "views": 2509, "likes": 228, "comments": 9, "shares": 2 }, "totalComments": 9 }
],
"meta": { "hasMore": false, "filters": { "account": ["dove"], "platform": ["tiktok"] } }
}
2

Snapshot the brand collection (sync)

Five posts, no search — this is a snapshot and returns 201 ready inline.

curl -sS -X POST -H "Authorization: Bearer $SIFTSY_API_KEY" -H "Content-Type: application/json" \
https://api.siftsy.app/analyst/v1/analyses \
-d '{ "selection": { "collectionId": "col_dove_brand" }, "include": ["wordCloud"] }'
{
"data": {
"id": "an_1c4e9d2a7b6f4a0e9d3c5b1f8a7e6d20",
"kind": "snapshot",
"status": "ready",
"selection": { "type": "collection", "collectionId": "col_dove_brand", "status": "complete" },
"filters": null,
"search": null,
"postCount": 5,
"scope": { "postCount": 5, "totalComments": 128, "matchedComments": 128, "matchedPosts": 5, "shareOfComments": 1, "shareOfContent": 1 },
"content": {
"postCount": 5,
"topics": [{ "title": "Limited-edition holiday scrubs", "postCount": 2 }, { "title": "Dove x Crumbl collaboration", "postCount": 1 },
{ "title": "Scent layering", "postCount": 1 }, { "title": "Deodorant — 72-hour protection", "postCount": 1 }],
"messages": [{ "topic": "Product discovery", "stance": "Dove's seasonal range is worth hunting down in store", "postCount": 5 }],
"scores": { "content": 7.4, "topic": 7.0 }
},
"commentAnalysis": {
"scores": { "sentiment": 7.0, "relevance": 8.2, "consensus": 7.6, "content": 7.4, "topic": 7.0 },
"mix": {
"sentiment": { "positivePercent": 38, "leanPositivePercent": 55, "neutralPercent": 0, "leanNegativePercent": 7, "negativePercent": 0 },
"relevance": { "positivePercent": 66, "leanPositivePercent": 25, "neutralPercent": 0, "leanNegativePercent": 7, "negativePercent": 2 },
"consensus": { "positivePercent": 52, "leanPositivePercent": 41, "neutralPercent": 0, "leanNegativePercent": 6, "negativePercent": 1 }
},
"wordCloud": [
{ "text": "please", "value": 31, "type": "word", "avgSentiment": 6.9, "sentimentCategory": "positive", "mentionPercentage": 24.2 },
{ "text": "uk", "value": 12, "type": "word", "avgSentiment": 6.1, "sentimentCategory": "positive", "mentionPercentage": 9.4 },
{ "text": "deodorant spray", "value": 9, "type": "phrase", "avgSentiment": 7.3, "sentimentCategory": "positive", "mentionPercentage": 7.0 }
],
"totalComments": 128,
"completeness": "scores_and_content"
},
"comments": { "path": "/analyses/an_1c4e9d2a7b6f4a0e9d3c5b1f8a7e6d20/comments" }
},
"meta": { "status": "ready" }
}

Reading it: the audience is warm (93% positive or lean-positive, no strongly negative bar), on-topic, and agreeing with each other. The word cloud already hints at what they are agreeing about — please, uk, deodorant spray: requests, not complaints.

3

Narrow to availability requests (filtered snapshot)

The dashboard would answer “how much of this is people asking where to buy it?” by filtering. Same thing here, and the analysis numbers describe only the matched comments:

curl -sS -X POST -H "Authorization: Bearer $SIFTSY_API_KEY" -H "Content-Type: application/json" \
https://api.siftsy.app/analyst/v1/analyses \
-d '{
"selection": { "collectionId": "col_dove_brand" },
"filters": {
"keywords": ["uk", "canada", "france", "australia", "available", "ship", "where"],
"relevance": ["on_topic", "relevant"],
"creator": "exclude"
}
}'
{
"data": {
"id": "an_7a2d0f9e3c1b4e8fa6d5c4b3a2918f01",
"kind": "snapshot",
"status": "ready",
"filters": { "relevance": ["positive", "lean_positive"], "keywords": ["uk", "canada", "france", "australia", "available", "ship", "where"], "keywordMatch": "any", "creator": "exclude" },
"scope": { "postCount": 5, "totalComments": 128, "matchedComments": 23, "matchedPosts": 3, "shareOfComments": 0.1797, "shareOfContent": 0.6 },
"content": {
"posts": [
{ "id": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "title": "The chaotic extrovert vs. the patient introvert #ScrubTok", "totalComments": 47, "matchedComments": 18 },
{ "id": "845b7346-8b22-43db-99a1-213b64f2a256", "title": "Coconut + vanilla scent layering", "totalComments": 21, "matchedComments": 3 },
{ "id": "b90d199b-178d-4471-8cf5-69df667fe7a8", "title": "Guess the scrub: Holiday Treats Edition", "totalComments": 23, "matchedComments": 2 }
]
},
"commentAnalysis": {
"scores": { "sentiment": 6.1, "relevance": 8.9, "consensus": 8.1 },
"mix": { "sentiment": { "positivePercent": 22, "leanPositivePercent": 61, "neutralPercent": 0, "leanNegativePercent": 17, "negativePercent": 0 } },
"notableComments": [{ "postId": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "commentId": "c_7583010641535831351_0412" }],
"totalComments": 128,
"completeness": "scores_and_content"
}
}
}

18% of the brand conversation is regional-availability asks, concentrated on the extrovert/introvert scrub video (18 of its 47 comments). Sentiment dips (6.1 vs 7.0) but consensus climbs — commenters are piling on in agreement that they want it shipped to them.

4

Search brand + influencers for PR and ambassador asks (job)

Now the question the influencer team cares about: across both collections, who is asking to be sent product or to work with Dove? That needs the AI search, so the request queues.

curl -sS -i -X POST -H "Authorization: Bearer $SIFTSY_API_KEY" -H "Content-Type: application/json" \
https://api.siftsy.app/analyst/v1/analyses \
-d '{
"selection": { "posts": { "account": "@dove,@skinbysam,@thescrubgirl,@jessdoesbeauty", "platform": "tiktok", "posted_since": "2025-12-01" } },
"search": { "query": "asking for PR packages, free samples, or to become a Dove ambassador or UGC creator", "minRelevance": 6 },
"filters": { "creator": "exclude", "replies": "exclude" }
}'
HTTP/1.1 202 Accepted
Location: /analyst/v1/analyses/an_9b8c7d6e5f4a4b3c2d1e0f9a8b7c6d55
Retry-After: 3
{
"data": {
"id": "an_9b8c7d6e5f4a4b3c2d1e0f9a8b7c6d55",
"kind": "search",
"status": "queued",
"selection": { "type": "posts", "posts": { "account": ["dove", "skinbysam", "thescrubgirl", "jessdoesbeauty"], "platform": ["tiktok"], "postedSince": "2025-12-01T00:00:00.000Z" }, "status": "complete" },
"search": { "query": "asking for PR packages, free samples, or to become a Dove ambassador or UGC creator", "mode": "ai", "minRelevance": 6 },
"postCount": 19,
"progress": { "phase": "queued", "percent": 0 },
"pollAfterMs": 3000,
"scope": null, "content": null, "commentAnalysis": null
},
"meta": { "status": "queued", "pollAfterMs": 3000 }
}

Poll the Location until it settles:

curl -sS -H "Authorization: Bearer $SIFTSY_API_KEY" \
"https://api.siftsy.app/analyst/v1/analyses/an_9b8c7d6e5f4a4b3c2d1e0f9a8b7c6d55"
{
"data": {
"id": "an_9b8c7d6e5f4a4b3c2d1e0f9a8b7c6d55",
"kind": "search",
"status": "ready",
"postCount": 19,
"scope": { "postCount": 19, "totalComments": 2214, "matchedComments": 131, "matchedPosts": 15, "shareOfComments": 0.0592, "shareOfContent": 0.7895 },
"content": {
"posts": [
{ "id": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "creator": { "username": "dove" }, "totalComments": 47, "matchedComments": 19 },
{ "id": "3e1f…", "creator": { "username": "skinbysam" }, "totalComments": 812, "matchedComments": 41 },
{ "id": "a9c2…", "creator": { "username": "thescrubgirl" }, "totalComments": 388, "matchedComments": 22 }
]
},
"commentAnalysis": {
"scores": { "sentiment": 7.1, "relevance": 8.4, "consensus": 7.9 },
"mix": { "sentiment": { "positivePercent": 68, "leanPositivePercent": 21, "neutralPercent": 0, "leanNegativePercent": 11, "negativePercent": 0 } },
"notableComments": [
{ "postId": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "commentId": "c_7583010641535831351_0107" },
{ "postId": "3e1f…", "commentId": "c_7588120034_0033" }
],
"totalComments": 131,
"completeness": "scores_and_content"
},
"comments": { "path": "/analyses/an_9b8c7d6e5f4a4b3c2d1e0f9a8b7c6d55/comments" }
},
"meta": { "status": "ready" }
}

On the brand posts alone, PR/ambassador asks are ~22% of comments (28 of 128) and 89% positive; the influencer posts dilute that share but add far more absolute volume — which is the list the partnerships team actually wants.

5

Pull the comments behind it

The comments route applies the search matches first, then returns a sample of those 131 comments — not the full set. Ask for notables:

curl -sS -H "Authorization: Bearer $SIFTSY_API_KEY" \
"https://api.siftsy.app/analyst/v1/analyses/an_9b8c7d6e5f4a4b3c2d1e0f9a8b7c6d55/comments?source=notables"
{
"data": [
{ "id": "c_7583010641535831351_0107", "postId": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "username": "scrubfan_92",
"text": "Dove please I beg you please make a whole body deodorant spray or perfume in the og dove deodorant powder scent 😭",
"engagement": { "likes": 212, "replies": 4 },
"scores": { "sentiment": 7.5, "relevance": 9, "consensus": 8 }, "buckets": { "sentiment": "positive", "relevance": "positive", "consensus": "positive" },
"sampleReasons": ["notable"] },
{ "id": "c_7583010641535831351_0412", "postId": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "username": "lu.in.london",
"text": "Dove PLEASE we need these in the UK. Please tell me there's at least a conversation at Dove HQ I'm having to get them shipped from the US 💔💔💔",
"engagement": { "likes": 96, "replies": 2 },
"scores": { "sentiment": 6, "relevance": 9, "consensus": 8.5 }, "buckets": { "sentiment": "lean_positive", "relevance": "positive", "consensus": "positive" },
"sampleReasons": ["notable"] },
{ "id": "c_7588120034_0033", "postId": "3e1f…", "username": "kate.reviews",
"text": "@dove pls send her the whole holiday scrub set, I need a full review 🙏",
"engagement": { "likes": 58, "replies": 1 },
"scores": { "sentiment": 8, "relevance": 8.5, "consensus": 8 }, "buckets": { "sentiment": "positive", "relevance": "positive", "consensus": "positive" },
"sampleReasons": ["notable", "content"] }
],
"meta": {
"sample": { "source": "notables", "limit": 25, "count": 25, "candidateCount": 131 },
"groups": {
"contentTopics": [
{ "postId": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "label": "Limited-edition holiday scrubs", "description": null, "commentCount": 19, "sampleCommentIds": ["c_7583010641535831351_0107", "c_7583010641535831351_0412"] },
{ "postId": "3e1f…", "label": "Limited-edition holiday scrubs", "description": null, "commentCount": 41, "sampleCommentIds": ["c_7588120034_0033"] }
],
"contentMessages": [
{ "postId": "fd7b30e5-9985-47fd-a105-1c607dc7490d", "label": "Product discovery", "description": "Dove's seasonal range is worth hunting down in store", "commentCount": 14, "sampleCommentIds": ["c_7583010641535831351_0412"] }
]
}
}
}

That sample is the intended use. Totals and mix live on the analysis (scope, commentAnalysis); do not page this route to reconstruct the thread. Mix-bar filters (?consensus=fighting) narrow the sample further.

Everything above is three request shapes — a selection, an analysis body, and a comments sample — applied to two collections. The same calls work for any brand and creator roster in your workspace.

Listing

GET /analyses?kind=all|snapshot|search|report lists newest first. snapshot and search are analyses you created here (with status, postCount, totalComments, matchedComments). report rows are campaign reports saved in Siftsy; they are always ready and expose the stored report at GET /analyses/{id}.

Limits

  • POST /analyses is on the analysis_create class (10 / minute, 100 / day), or analysis_search when search.mode is ai (2 / minute, 10 / day). GET /analyses/{id} is analysis_read (60 / minute, 2,000 / day). GET /analyses/{id}/comments is analysis_comments (20 / minute, 300 / day) — every page re-reads the comments of every post on the analysis, so take one source=notables sample per analysis rather than paging through it. See Rate limits.
  • 1,000 posts per selection; 100 posts for the synchronous path.
  • 50 values per list filter, 120 characters per keyword, 300 characters per search query.
  • Analyses expire 7 days after creation and return 404 afterwards.