Track topics and hashtags
Search several topics and their hashtags on TikTok, Instagram, YouTube and X every few hours, judge every post against each topic and get only the posts no earlier run returned
Polling each hashtag on each platform returns the same posts again on every poll, posts that use the hashtag for something else, and a different format per platform. One scheduled discover request replaces those polls. Each topic is a group with its keywords and hashtags, every post is judged against the topic, repeats are removed across platforms, and each run returns only the posts that no earlier run returned.
The request
{
"groups": [
{
"id": "georgia",
"keywords": ["Georgia General Assembly", "Georgia election 2026", "Georgia cost of living", "#gapol", "#georgiapolitics"],
"context": "Posts about politics in the US state of Georgia: the General Assembly, the 2026 election, abortion law and the cost of living in Georgia"
},
{
"id": "groceries",
"keywords": ["grocery prices", "shrinkflation", "SNAP benefits", "#groceryprices", "#foodstamps"],
"context": "Posts about rising grocery prices, shrinkflation, and SNAP or food stamp benefits in the United States"
},
{
"id": "maga",
"keywords": ["MAGA", "Trump administration", "America First", "#maga", "#americafirst"],
"context": "Posts about President Trump, his administration or the MAGA movement, from supporters or critics"
}
],
"platforms": ["tiktok", "instagram", "youtube", "twitter"],
"period": "2d",
"label": ["stance"],
"max_credits": 1200,
"schedule": { "every": "6h", "max_credits_per_run": 1200, "max_credits_per_month": 30000, "name": "Topics" }
}# request.json is the body above: "groups": [...], "schedule": {...}, "max_credits": 1200
curl -X POST "https://www.socialcrawl.dev/v1/prism/discover" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: topics-6h" \
--data @request.jsonSend it once with "dry_run": true and without schedule to see every search and the estimate, free. The real request answers with data.schedule (its schedule_id, next_fire_at and links.runs_url) and starts the first run at once.
What each part does
- Write a hashtag as a
#tagkeyword inside the group. On Instagram, TikTok and YouTube it runs both the platform's hashtag search and its keyword search, and on X it searches the hashtag as text. A group has nohashtagsfield. contextis what a kept post must be about. A post that uses#magaor#gapolfor something else is dropped asoff_topic, so one group's hashtag noise never reaches your data.periodcounts calendar days intz(UTC by default), so1dis today only and a run just after midnight would see minutes of posts.2dcovers the gap, and the watermark below stops repeats.label: ["stance"]labels each kept postpositive,negative,neutralorno_opiniontoward its group's keywords, andnullwhen the judge is unsure.summary.by_group.<id>.stancecounts them per topic.schedule.everytakes1h,6h,12h,1d,7dor30d. Sending the same request again from the same key returns the schedule that already exists (deduplicated: true) and starts nothing.
Only new posts on every run
Each scheduled run starts from the previous run's watermark, so a post that an earlier run returned is dropped before it is judged and never billed again. summary.dropped.by_filter["filter:watermark"] counts them. A scheduled request also continues from a one-off run of the same request, so a test run you made first is not delivered twice.
Measured on 20/10/2026 with the three groups above and "period": "1d":
| Run | Posts returned by the searches | Kept | Dropped as already delivered | Credits |
|---|---|---|---|---|
| One-off run, 16:21 UTC | 2,232 | 440 | 0 | 209 |
| First scheduled run, 16:31 UTC | 2,175 | 124 | 1,006 | 139 |
| Second scheduled run, 17:32 UTC | 2,274 | 162 | 961 | 151 |
On the one-off run the searches returned 2,232 posts. Of those, 299 were exact repeats across searches, 977 were outside the window and 497 were off-topic, so 440 were kept. The one-off run took about 6 minutes.
What a run costs
The searches cost the same on every run, and judging is paid only for posts not judged before. Per platform, on the one-off run:
| Platform | Search credits | Kept posts | Credits per kept post |
|---|---|---|---|
| YouTube | 21 | 213 | 0.10 |
| X | 14 | 127 | 0.11 |
| TikTok | 20 | 64 | 0.31 |
| 59 | 36 | 1.64 |
Instagram's hashtag search costs 5 credits a page and its reels search returned few posts inside one day, so Instagram cost about 60 of each run's 115 search credits and kept 1 of the 124 new posts on the first scheduled run and 13 of the 162 on the second. Keep Instagram for the topics where you need it, or put it in a group of its own on a slower schedule. summary.by_platform.<platform>.credits_per_kept shows this for your own topics after every run.
max_credits_per_run must cover the dry run's worst case (estimate.credits.max, 1,143 for this request), so that no run stops halfway; a lower cap is a free 400 whose suggested_value is the number to send. The runs above spent 139 to 209. max_credits_per_month is the budget. A run that could take the month past it is skipped and the schedule pauses until the next month.
Reading the posts
GET {links.runs_url}lists the schedule's runs, newest first, each withstatus,credits.spentandstatus_url.GET {status_url}/results.jsonlstreams a settled run's kept posts, one per line, free. Each post carriesgroup,platform,url,published_at,author.key,engagement,matched_keywords,hashtags,judgments.relevance.pandjudgments.stance.label. Store posts byid, which stays the same across runs.- Split the export on
\nonly. Bios and captions can contain the characters U+2028 and U+0085, and a split that treats them as line ends (Python'ssplitlines()) cuts a JSON line in half. - Each run that keeps posts sends
discover.new_rowswith the count and the first 100 new ids. Register a URL once withPOST /v1/webhook-endpointsand"types": ["discover.*"](Event webhooks), or read the same events withGET /v1/events?types=discover.new_rows&cursor=oldest(Events). - An account runs two background runs at once. A scheduled run that starts while two others are running waits as
queuedand starts by itself.
Hashtags you are not searching yet
view=items on a week of a topic's posts lists the hashtags those posts use, with their count per platform and their growth over the week:
curl -G "https://www.socialcrawl.dev/v1/prism/discover" \
-H "x-api-key: YOUR_API_KEY" \
--data-urlencode "keywords=Georgia General Assembly,Georgia election 2026,Georgia cost of living,#gapol,#georgiapolitics" \
--data-urlencode "context=Posts about politics in the US state of Georgia: the General Assembly, the 2026 election, abortion law and the cost of living in Georgia" \
--data-urlencode "view=items" \
--data-urlencode "platforms=tiktok,instagram,youtube,twitter" \
--data-urlencode "period=7d" \
--data-urlencode "max_credits=150"On 20/10/2026 it returned 268 items for 45 credits. The hashtags the group did not search included #JonOssoff (19 posts on four platforms, growth 5.33), #election2026 (14), #KeishaLanceBottoms (8, growth 7), #earlyvoting (7) and #GeorgiaSenate (3). Add the ones that belong to the topic as #tag keywords; sending the schedule's request with new keywords creates a new schedule, so delete the old one with DELETE /v1/prism/discover/schedules/{schedule_id}.
Manage the schedule
| Call | What it does |
|---|---|
GET /v1/prism/discover/schedules/{schedule_id} | The schedule, its last 10 runs and this month's spend. |
POST /v1/prism/discover/schedules/{schedule_id}/pause | Stops new runs until you resume it. |
POST /v1/prism/discover/schedules/{schedule_id}/resume | Starts it again, optionally with new caps. |
DELETE /v1/prism/discover/schedules/{schedule_id} | Deletes it; its runs keep their posts until their retention ends. |
All four are free. See Schedules for skipped slots and pauses.
