Social listening with search endpoints

One term, five searches, each with its own filter rules and its own page of posts.

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
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"
Python
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"])
TypeScript
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
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"
Python
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"])
TypeScript
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
curl -G "https://api.socialscrape.io/v1/instagram/search/reels" \
  -H "x-api-key: $SOCIALSCRAPE_API_KEY" \
  --data-urlencode "query=sourdough"
Python
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"])
TypeScript
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.

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.

Questions

Is this a firehose?

No. Each call is one page of that platform's public search. X search is engine-indexed posts, not X's own search, so recent posts can be missing. Threads search does not page.

Can I sort TikTok search by latest?

No. Logged out, sort other than relevance is refused. date_posted drops videos older than the window from each page. Pages can come back short. Follow has_more.

Does YouTube search accept sort=latest?

The parameter exists and is refused. YouTube answers latest and rating in relevance order, so the API returns invalid_params. Use relevance or popular, and narrow with upload_date.

How do I hear about new posts without polling?

Create a monitor on a list endpoint. It sends new items to a signed webhook. See monitors in the docs.

Build on social data early.

Developers and agent builders get the first keys.

Get early access