Social listening here means a narrow job: search public posts for a term, store the ids, and run the search again later. It is not a purchased stream of every mention on the internet. Each platform has its own search route, its own page size and its own filter rules. The calls below are in beta. The key is x-api-key. See authentication.
Keep a row per platform and post id. A later run inserts only ids you do not have. That is the whole dedupe. Do not merge five rankings into one "top post". Each platform ranked its own page.
TikTok videos#
GET /v1/tiktok/search/keyword searches videos. query is required. The recorded request also sends date_posted=week.
Logged out, TikTok ignores sort and date filters in the way the reference spells out. sort other than relevance is refused. date_posted (any, day, week, month, three_months, six_months) drops videos older than the window from each page. Pages can be short or empty. Keep following has_more. region is TikTok's region hint, not a proxy country.
curl -G "https://api.socialscrape.io/v1/tiktok/search/keyword" \
-H "x-api-key: $SOCIALSCRAPE_API_KEY" \
--data-urlencode "query=nasa" \
--data-urlencode "date_posted=week"import os
import requests
params = {
"query": "nasa",
"date_posted": "week",
}
res = requests.get(
"https://api.socialscrape.io/v1/tiktok/search/keyword",
headers={"x-api-key": os.environ["SOCIALSCRAPE_API_KEY"]},
params=params,
)
body = res.json()
if body["ok"]:
print(body["data"])
else:
print(body["error"]["code"], body["error"]["message"])const url = new URL("https://api.socialscrape.io/v1/tiktok/search/keyword");
url.search = new URLSearchParams({
"query": "nasa",
"date_posted": "week"
}).toString();
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALSCRAPE_API_KEY! },
});
const body = await res.json();
if (body.ok) {
console.log(body.data);
} else {
console.error(body.error.code, body.error.message);
}Items are posts: id, url, text, published_at, author, stats (views, likes, comments, shares), hashtags, song. extra can include author_followers, comments_disabled and is_private. Store id and url. The url is what you pass to GET /v1/tiktok/video/comments if you want the comments on that video.
Each page is 1 credit. Pass cursor back until has_more is false. See pagination.
YouTube#
GET /v1/youtube/search takes query. type is video, channel, playlist, short or live. Shorts and live results are posts of type short and live. upload_date is hour, today, week, month or year. duration (short, medium, long) applies to videos, not Shorts.
sort defaults to relevance. latest and rating are refused with invalid_params, because YouTube answers them in relevance order. Use relevance or popular (view count) and narrow with upload_date. include_extras adds likes, comment count and description per video.
The recorded request asks for playlists, so the sample items are collections (kind, title, owner, item_count), not videos. For a listening job, set type to video. The same query string works. Playlist rows are the wrong object if you wanted captions and view counts.
curl -G "https://api.socialscrape.io/v1/youtube/search" \
-H "x-api-key: $SOCIALSCRAPE_API_KEY" \
--data-urlencode "query=lofi hip hop" \
--data-urlencode "type=playlist"import os
import requests
params = {
"query": "lofi hip hop",
"type": "playlist",
}
res = requests.get(
"https://api.socialscrape.io/v1/youtube/search",
headers={"x-api-key": os.environ["SOCIALSCRAPE_API_KEY"]},
params=params,
)
body = res.json()
if body["ok"]:
print(body["data"])
else:
print(body["error"]["code"], body["error"]["message"])const url = new URL("https://api.socialscrape.io/v1/youtube/search");
url.search = new URLSearchParams({
"query": "lofi hip hop",
"type": "playlist"
}).toString();
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALSCRAPE_API_KEY! },
});
const body = await res.json();
if (body.ok) {
console.log(body.data);
} else {
console.error(body.error.code, body.error.message);
}Page with cursor. One page, one credit.
Instagram reels, Threads, X#
GET /v1/instagram/search/reels searches reels for query, from Instagram's public Popular search. The reference says that is the same source as search/popular, not Google's index, about 50 to 70 reels over 5 or 6 pages. date_posted is accepted and not applied. There is no date filter upstream. The recorded query is sourdough.
curl -G "https://api.socialscrape.io/v1/instagram/search/reels" \
-H "x-api-key: $SOCIALSCRAPE_API_KEY" \
--data-urlencode "query=sourdough"import os
import requests
params = {
"query": "sourdough",
}
res = requests.get(
"https://api.socialscrape.io/v1/instagram/search/reels",
headers={"x-api-key": os.environ["SOCIALSCRAPE_API_KEY"]},
params=params,
)
body = res.json()
if body["ok"]:
print(body["data"])
else:
print(body["error"]["code"], body["error"]["message"])const url = new URL("https://api.socialscrape.io/v1/instagram/search/reels");
url.search = new URLSearchParams({
"query": "sourdough"
}).toString();
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALSCRAPE_API_KEY! },
});
const body = await res.json();
if (body.ok) {
console.log(body.data);
} else {
console.error(body.error.code, body.error.message);
}GET /v1/threads/search returns one page of about 15 to 20 posts. It does not page. start_date and end_date filter that page only. Threads does not apply them itself. Details are in Threads profiles, posts and search.
GET /v1/twitter/search/posts finds public posts through a web search engine, restricted to x.com, then reads them like a tweet. The reference is blunt: this is engine-indexed discovery, not X's own search, which needs a login. Coverage is incomplete, recent posts can be missing, and the order is the engine's. Views, retweet and bookmark counts are missing. Deleted, protected and unreachable posts are left out. Cursor pages 1 to 11. The recorded query is ai agents. Errors include upstream_blocked as well as invalid_params and not_found.
A hit is still a post: text, author, published_at, stats with the gaps above, url. Store the id. Do not describe the list as everything said on X that day.
After the search#
Comments are a second call, per video, and another credit. Instagram comment pages stop at about 14 and do not page. That limit is Get Instagram post comments. TikTok and YouTube comment routes are separate and do page. Check has_more on the route you called. Do not assume they share Instagram's rule.
A monitor watches a list endpoint and posts new items to a signed webhook, so you are not polling. Create one with the list path and that endpoint's params. Management calls are free. The contract is monitors and monitors and webhooks.
Cost and limits#
Every successful search page is 1 credit. Five platforms, one page each, is five credits if nothing was cached. max_age_days makes a repeat of the same query free and is the wrong tool if you need posts from the last hour. Use it when yesterday's page is acceptable. Cache TTL on these routes is 30 days. Failures cost 0.
Signup is 100 credits, once, no card. See pricing and credits.
The honest gaps: TikTok will not sort by latest while logged out. YouTube will not sort by latest either. Instagram will not filter reels by date. Threads will not give you page two. X will not give you the firehose. Write those down next to the term you track, or the report will treat missing as silence.
Free credits are 30 requests a minute per team. See rate limits.
Next#
The product page for this job is the social listening use case. Endpoints: TikTok search, YouTube search, Instagram reel search, Threads search, X post search. Tutorials follow the same slugs under /tutorials/how-to-scrape-..., for example how to scrape TikTok search. References live under the API index.