Read the comments under many posts
Get the comments under up to 25 post URLs from eight platforms in one call, then poll the same list for new comments only.
You will send a list of post URLs and get each post's comments back in one call, then turn the same call into a poll that returns only the comments posted since your last run. The posts can come from Instagram, TikTok, YouTube, Facebook, Reddit, X, Threads and LinkedIn, mixed in one list.
Cost per run: each post costs what its own comment endpoint charges per page: 1 credit on TikTok, YouTube, Facebook, X and Threads, 5 on Instagram, LinkedIn and Reddit. The call below reads 20 comments from three posts and costs 7 credits. A link that is not a post, and a post with no comments, cost nothing.
How do I get the comments from many posts at once?
Send the post URLs to POST /v1/prism/post-comments:
curl -X POST "https://www.socialcrawl.dev/v1/prism/post-comments" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"per_post": 20, "items": ["https://www.tiktok.com/@mrbeast/video/7654638524729216287", "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "https://www.reddit.com/r/socialmedia/comments/1qca2ki/social_listening_tool/"]}'Each URL gets one row in data.results, in the order you sent them. per_post sets how many comments are read per post, 20 by default and up to 200. You can send up to 25 URLs per call.
What you get back
One row per URL. An ok row holds the post's comments in data.items, in the same shape that platform's own comment endpoint returns. A URL that is not a post comes back unsupported at 0 credits, and the other rows are not affected.
{
"success": true,
"endpoint": "/v1/prism/post-comments",
"data": {
"results": [
{
"index": 0,
"custom_id": "campaign-7",
"target": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"status": "ok",
"data": {
"platform": "youtube",
"endpoint": "/v1/youtube/video/comments",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"items": [
{
"comment": {
"id": "UgxwNNVdFXL7B03gU3R4AaABAg",
"post_id": "dQw4w9WgXcQ",
"text": "did anyone else come from the feodoric video",
"author": { "username": "@Ky4tsuk1_Bakuh0" },
"engagement": { "likes": 0, "replies": 0 },
"published_at": "2026-10-25T22:50:22.000Z"
}
}
],
"truncated": true,
"next_cursor": null,
"pages": 1
},
"cost": 1
},
{
"index": 1,
"target": "https://example.com/not-a-post",
"status": "unsupported",
"error": {
"type": "UNSUPPORTED_URL",
"message": "Not a post URL on a platform with a comment endpoint (instagram, tiktok, youtube, facebook, reddit, twitter, threads, linkedin). Pass the link to one post, video or tweet."
},
"cost": 0
}
],
"summary": {
"total": 2,
"ok": 1,
"not_found": 0,
"unsupported": 1,
"error": 0,
"deferred": 0,
"coverage": 0.5,
"credits_charged": 1,
"credits_refunded": 0
}
},
"credits_used": 1
}That response is trimmed to one comment and the main comment fields. Each row tells you:
status:ok,not_found(the post cannot be found),unsupported(not a post URL on one of the eight platforms),errorordeferred. Onlyokrows cost credits.cost: the credits that row cost.credits_usedon the response is the sum, and every page that was not read is refunded.truncatedandnext_cursor:truncatedistruewhen the post has more comments than the row holds. Sendnext_cursorascursortodata.endpointto read the next page. It isnullwhen the post has no more comments, and also when the read was cut atper_post; in that case raiseper_postto read further.custom_id: any id you attach to an item (up to 128 characters) comes back on its row, so you can match rows to your own records.
How do I get only the new comments?
Add since. Each row then keeps only the comments published at or after it, and a post with no new comments costs nothing. This program prints the comments posted in the last 24 hours under three posts:
// poll-comments.ts
// Prints the comments posted in the last 24 hours under a list of posts.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx poll-comments.ts
const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");
const BASE = "https://www.socialcrawl.dev";
const posts = [
{ url: "https://www.tiktok.com/@mrbeast/video/7654638524729216287", custom_id: "mrbeast-tiktok" },
{ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", custom_id: "rickroll-youtube" },
{ url: "https://www.instagram.com/p/CnpPou9hWqq/", custom_id: "nasa-instagram" },
];
const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
interface Row {
index: number;
custom_id?: string;
status: string;
cost: number;
data?: {
items: { comment: { text: string; author: { username: string | null } } }[];
};
}
async function newComments(items: typeof posts) {
const res = await fetch(`${BASE}/v1/prism/post-comments`, {
method: "POST",
headers: { "x-api-key": KEY!, "Content-Type": "application/json" },
body: JSON.stringify({ items, since, per_post: 50 }),
});
const json = (await res.json()) as {
success: boolean;
data?: { results: Row[] };
credits_used: number;
error?: { message: string };
};
if (!json.success) throw new Error(json.error?.message ?? `HTTP ${res.status}`);
return { rows: json.data!.results, credits: json.credits_used };
}
const first = await newComments(posts);
// A post not started within the 45-second limit comes back "deferred" and
// costs nothing. Send those posts again.
const deferred = first.rows.filter((r) => r.status === "deferred").map((r) => posts[r.index]!);
const second = deferred.length > 0 ? await newComments(deferred) : { rows: [], credits: 0 };
for (const row of [...first.rows.filter((r) => r.status !== "deferred"), ...second.rows]) {
if (row.status !== "ok") {
console.log(`${row.custom_id}: ${row.status}`);
continue;
}
console.log(`${row.custom_id}: ${row.data!.items.length} new comments, ${row.cost} credits`);
for (const { comment } of row.data!.items.slice(0, 3)) {
console.log(` ${comment.author.username}: ${comment.text.slice(0, 80)}`);
}
}
console.log(`Charged ${first.credits + second.credits} credits since ${since}`);Instagram, YouTube, X and LinkedIn posts are read newest first, so the read stops at the first comment older than since. TikTok, Facebook, Reddit and Threads posts are read up to per_post comments and then filtered, so on a busy post raise per_post to make sure nothing new is missed. Run the program on a schedule and pass the time of your last run as since.
What to change
- The list: up to 25 post URLs per call. For more, split the list, or run up to 1,000 posts in the background with
POST /v1/prism/jobsand that platform's comment endpoint (see Async requests). per_post: how deep each post is read, from 1 to 200. Deeper reads cost more pages on busy posts.- Retries: send an
Idempotency-Keyheader, and a retry of a call that timed out returns the first answer at 0 credits instead of running again. See Idempotency. - Streaming: send
Accept: text/event-streamto get each post's row as soon as it is read. See Streaming (SSE).
What to know about Reddit
A Reddit row holds the comments of one read of the thread. GET /v1/reddit/post/comments on a single thread also expands deeper reply trees, so use it when you need the full tree of one thread. A Reddit thread can take 20 to 30 seconds to read, so a list with many Reddit posts can come back partly deferred. Send those posts again, or run the list as a background job.
