SocialCrawl

Recipes

Six working recipes that solve real cross-platform tasks — universal search, multi-platform transcripts, ad-library aggregation, comparable engagement scoring, music breakouts, and hybrid search-then-enrich.

Recipes

Each recipe below solves one specific task you'd arrive with. Code is TypeScript, runs on Node 22+, uses native fetch, and assumes SOCIALCRAWL_KEY is in your environment. Every snippet is copy-paste runnable — handles, queries, and URLs are real.

Cost callouts appear in every recipe so you can budget before you build.

RecipeWhat you'll buildCost per run
1. Pulse-check any topic across the social webStreaming search over 12 platforms with comments enriched20 credits
2. Transcribe video from anywhereOne function, seven platforms10 credits per video
3. Aggregate every ad a brand is runningParallel fan-out across four ad networks20 credits
4. Score creator engagement comparably across platformsApples-to-apples engagement-rate ranking3 credits per creator
5. Spot a song breaking outCross-platform music heat score3 credits per song
6. Hybrid search-then-enrichUniversal search plus per-platform transcripts120 credits

Recipe 1 — Pulse-check any topic across the social web

Build a streaming "what is the internet saying right now about X" feed for a brand-monitoring dashboard, an editorial newsroom, or an investor screening a thesis.

Endpoints used

  • GET /v1/search/everywhere — fans out across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, Threads, Pinterest, Perplexity, and Tavily; returns ranked + clustered results with the top comments from each post (cost: 20cr flat)

Cost per run: 20 credits regardless of how many sources respond.

The endpoint supports two response modes selected by the Accept header. Streaming is the default below because chunks arrive as enrichment lands — you can render results before the request finishes.

// recipe-1-pulse-check.ts
// Streams universal search results for a single topic.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-1-pulse-check.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

const url = new URL("https://www.socialcrawl.dev/v1/search/everywhere");
url.searchParams.set("query", "anthropic claude 4");
url.searchParams.set("lookback_days", "7");

const res = await fetch(url, {
  headers: {
    "x-api-key": KEY,
    accept: "text/event-stream",
  },
});

if (!res.ok || !res.body) {
  throw new Error(`Search failed: ${res.status} ${await res.text()}`);
}

const decoder = new TextDecoder();
let buffer = "";

for await (const chunk of res.body) {
  buffer += decoder.decode(chunk as Uint8Array, { stream: true });
  const events = buffer.split("\n\n");
  buffer = events.pop() ?? "";

  for (const event of events) {
    const dataLine = event.split("\n").find((line) => line.startsWith("data: "));
    if (!dataLine) continue;
    const payload = JSON.parse(dataLine.slice(6));

    switch (payload.type) {
      case "plan_refined":
        console.log("plan ready, sub-queries:", payload.plan.subqueries.length);
        break;
      case "clusters":
        console.log(`clustered into ${payload.clusters.length} themes`);
        break;
      case "comments_enriched":
        for (const item of payload.items) {
          const top = item.source_items[0]?.metadata?.top_comments?.[0];
          console.log(`[${item.source}] ${item.title}`);
          if (top) console.log(`  → "${top.text.slice(0, 120)}…"`);
        }
        break;
      case "done":
        console.log(
          `done — ${payload.summary.total_items} results from ${payload.summary.sources_called.length} sources, charged ${payload.summary.credits_used}cr`,
        );
        break;
      case "error":
        console.error("search error:", payload.message);
        break;
    }
  }
}

If you don't need streaming, switch the Accept header to application/json and the same endpoint returns the full ranked set in one response body — same data, no chunk handling.

What you get back

// One `comments_enriched` SSE chunk — many of these arrive over the stream
{
  "type": "comments_enriched",                  // <-- discriminator
  "items": [
    {
      "candidate_id": "rd_18gha22",
      "source": "reddit",                       // <-- one of the 12 platforms
      "title": "Claude 4 is shockingly good at refactors",
      "url": "https://reddit.com/r/ClaudeAI/comments/...",
      "snippet": "I gave it a 4k-line Rust file and...",
      "final_score": 0.91,                      // <-- RRF + rerank fused
      "cluster_id": "cluster_3",                // <-- groups related posts
      "source_items": [{
        "metadata": {
          "top_comments": [                     // <-- real-people sentiment
            { "text": "Same. Just shipped a migration in 20 min.", "score": 142 }
          ]
        }
      }]
    }
  ]
}

Take it further

  • See Universal social search for the full chunk schema, narrowing with sources= / exclude=, and date-bounded queries.
  • Swap query to a brand, a competitor, a person, a meme, a vertical, an asset — the endpoint doesn't care.
  • Next: Recipe 6 — Hybrid search-then-enrich feeds these results into per-platform transcripts.

Recipe 2 — Transcribe video from anywhere

Build a podcast / video-research tool that takes any social video URL and returns its spoken content, regardless of which platform it lives on.

Endpoints used (all premium, 10cr each)

  • GET /v1/tiktok/post/transcript — TikTok video transcript
  • GET /v1/instagram/media/transcript — Instagram reel / video transcript
  • GET /v1/youtube/video/transcript — YouTube / Shorts transcript
  • GET /v1/facebook/post/transcript — Facebook post / reel transcript
  • GET /v1/twitter/tweet/transcript — X video tweet transcript
  • GET /v1/reddit/post/transcript — Reddit video-post transcript
  • GET /v1/rumble/video/transcript — Rumble video transcript

Cost per run: 10 credits per video. Fanning out a single video across all seven platforms would cost 70 credits — in practice you call exactly one of these per video because each platform only hosts its own content.

Transcript endpoints are upstream-pass-through (the canonical Transcript archetype keeps the upstream shape so you don't lose platform-specific fields). The three shapes verified live:

  • YouTubedata.transcript[] (array of {text, startMs, endMs, startTimeText} segments) + data.transcript_only_text (string) + data.language (string)
  • TikTokdata.transcript is a string (the full text directly)
  • Instagramdata.transcripts[] (note the plural) of {id, shortcode, text}

The other four platforms (Facebook, Twitter, Reddit, Rumble) follow the same upstream-pass-through pattern. The extractText() adapter in the snippet below handles the variance.

// recipe-2-transcribe.ts
// Returns the transcript of any social video URL.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-2-transcribe.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

type Platform =
  | "tiktok"
  | "instagram"
  | "youtube"
  | "facebook"
  | "twitter"
  | "reddit"
  | "rumble";

const ENDPOINT: Record<Platform, string> = {
  tiktok: "tiktok/post/transcript",
  instagram: "instagram/media/transcript",
  youtube: "youtube/video/transcript",
  facebook: "facebook/post/transcript",
  twitter: "twitter/tweet/transcript",
  reddit: "reddit/post/transcript",
  rumble: "rumble/video/transcript",
};

// Adapter that normalises the per-platform shape into a single text string.
// Each branch reflects the upstream's actual response (verified live).
function extractText(platform: Platform, data: Record<string, unknown>): string {
  if (platform === "youtube") {
    return (data.transcript_only_text as string) ?? "";
  }
  if (platform === "tiktok") {
    return (data.transcript as string) ?? "";
  }
  if (platform === "instagram") {
    const t = data.transcripts as Array<{ text?: string }> | undefined;
    return t?.[0]?.text ?? "";
  }
  // Facebook / Twitter / Reddit / Rumble: same upstream-pass-through pattern.
  // Inspect the response on first call and extend this switch as needed.
  return (
    (data.transcript_only_text as string) ??
    (data.transcript as string) ??
    ""
  );
}

async function transcribe(platform: Platform, videoUrl: string) {
  const url = new URL(`https://www.socialcrawl.dev/v1/${ENDPOINT[platform]}`);
  url.searchParams.set("url", videoUrl);

  const res = await fetch(url, { headers: { "x-api-key": KEY! } });
  const json = (await res.json()) as {
    success: boolean;
    data?: Record<string, unknown>;
    credits_remaining: number;
    error?: { message: string };
  };

  if (!json.success) throw new Error(json.error?.message ?? "transcript failed");
  return {
    platform,
    text: extractText(platform, json.data!),
    credits_remaining: json.credits_remaining,
  };
}

// Same input shape, same output shape, three different platforms.
const a = await transcribe(
  "youtube",
  "https://www.youtube.com/watch?v=erLbbextvlY",
);
const b = await transcribe(
  "tiktok",
  "https://www.tiktok.com/@mrbeast/video/7283145247503961371",
);
const c = await transcribe(
  "rumble",
  "https://rumble.com/v5o1eum-the-rubin-report-with-elon-musk.html",
);

for (const t of [a, b, c]) {
  console.log(`[${t.platform}] ${t.text.length} chars`);
  console.log(t.text.slice(0, 200), "…");
  console.log("---");
}

What you get back

// YouTube — segments + full string + ISO language code
{
  "data": {
    "transcript": [                                                              // <-- array of segments
      { "text": "we are now stranded on this deserted", "startMs": "80", "endMs": "4000", "startTimeText": "0:00" }
      // ... 620 more
    ],
    "transcript_only_text": "we are now stranded on this deserted island...",   // <-- concatenated string
    "language": "en-US"
  }
}

// TikTok — transcript is the string directly
{
  "data": {
    "id": "7647161577057258775",
    "url": "https://www.tiktok.com/@...",
    "transcript": "claude is shockingly good at refactors..."                   // <-- full text as string
  }
}

// Instagram — note the PLURAL key
{
  "data": {
    "transcripts": [                                                             // <-- plural, array
      { "id": "DY7SXBFtEpC", "shortcode": "DY7SXBFtEpC", "text": "..." }
    ]
  }
}

Take it further

  • See Computed fields for the language-detection logic that backs transcript.language when upstream doesn't tag it.
  • Swap youtube for any of the seven platform keys above — the function signature doesn't change.
  • Next: Recipe 6 — Hybrid search-then-enrich wires transcribe() to Recipe 1's search output.

Recipe 3 — Aggregate every ad a brand is running

Build a competitive-intelligence dashboard that shows every ad a target company is running across Meta, Google, LinkedIn, and Reddit — for sales-call prep, market-research reports, or buying-team intake.

Endpoints used (all advanced, 5cr each)

  • GET /v1/facebook/adlibrary/search/ads — Meta Ad Library keyword search (param: query)
  • GET /v1/google/company/ads — Google Ad Transparency Library by advertiser (param: domain or advertiser_id)
  • GET /v1/linkedin/ads/search — LinkedIn Ad Library keyword search (param: query)
  • GET /v1/reddit/ads/search — Reddit Ad Library keyword search (param: query)

Cost per run: 20 credits per brand audit (4 networks × 5cr).

The four ad-library responses have different upstream shapes; this recipe flattens them into a unified { network, ad } row so you can render them in one table.

// recipe-3-ad-audit.ts
// Pulls every ad a brand is running across 4 networks in parallel.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-3-ad-audit.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

const BASE = "https://www.socialcrawl.dev/v1";
const brand = "stripe";

async function get(path: string, params: Record<string, string>) {
  const url = new URL(`${BASE}/${path}`);
  for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
  const res = await fetch(url, { headers: { "x-api-key": KEY! } });
  if (!res.ok) return { success: false, error: await res.text() };
  return (await res.json()) as {
    success: boolean;
    data?: { items?: unknown[] };
    credits_remaining: number;
  };
}

type AdRow = { network: string; ad: unknown };

// Google ad-library searches by domain or advertiser_id — not free-text.
// Brand → domain is the most common shape; map your input accordingly.
const brandDomain = "stripe.com";

const [meta, google, linkedin, reddit] = await Promise.all([
  get("facebook/adlibrary/search/ads", { query: brand }),
  get("google/company/ads", { domain: brandDomain }),
  get("linkedin/ads/search", { query: brand }),
  get("reddit/ads/search", { query: brand }),
]);

const ads: AdRow[] = [
  ...(meta.data?.items ?? []).map((ad) => ({ network: "meta", ad })),
  ...(google.data?.items ?? []).map((ad) => ({ network: "google", ad })),
  ...(linkedin.data?.items ?? []).map((ad) => ({ network: "linkedin", ad })),
  ...(reddit.data?.items ?? []).map((ad) => ({ network: "reddit", ad })),
];

console.log(`${ads.length} ads found for "${brand}"`);
const counts = ads.reduce<Record<string, number>>(
  (acc, row) => ({ ...acc, [row.network]: (acc[row.network] ?? 0) + 1 }),
  {},
);
console.table(counts);

console.log(`credits left: ${reddit.credits_remaining}`);

What you get back

// Final aggregated shape after the flattening loop:
[
  {
    "network": "meta",
    "ad": {
      "id": "ad_1234567890",
      "page_name": "Stripe",
      "ad_creative_body": "Accept payments online in minutes...",
      "first_active": "2026-05-01",            // <-- normalised from raw upstream
      "active": true
    }
  },
  {
    "network": "linkedin",
    "ad": {
      "id": "ln_987654321",
      "advertiser_name": "Stripe",
      "headline": "Built for fast-growing teams",
      "creative_url": "https://www.linkedin.com/ads/..."
    }
  }
  // ... google + reddit rows mixed in
]

Take it further


Recipe 4 — Score creator engagement comparably across platforms

Build a creator-vetting tool for an influencer-marketing team that needs to compare a creator's TikTok performance against their Instagram and YouTube on a single ranked dashboard.

Endpoints used (all standard, 1cr each)

  • GET /v1/tiktok/profile — TikTok profile with computed.engagement_rate
  • GET /v1/instagram/profile — Instagram profile with computed.engagement_rate
  • GET /v1/youtube/channel — YouTube channel with computed.engagement_rate

Cost per run: 3 credits per creator.

This recipe shows the payoff for SocialCrawl's canonical schema: every platform's engagement_rate is computed against the same formula and clamped into [0, 1], so a TikTok 0.082 is directly comparable to an Instagram 0.082.

// recipe-4-engagement-table.ts
// Compares one creator's engagement across TikTok, Instagram, and YouTube.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-4-engagement-table.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

const BASE = "https://www.socialcrawl.dev/v1";
const handle = "mrbeast";

type Profile = {
  success: boolean;
  platform: string;
  data?: {
    author: { username: string | null; followers: number | null };
    computed: {
      engagement_rate: number | null;
      estimated_reach: number | null;
      language: string | null;
      content_category: string | null;
    };
  };
};

async function get(path: string, params: Record<string, string>): Promise<Profile> {
  const url = new URL(`${BASE}/${path}`);
  for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
  const res = await fetch(url, { headers: { "x-api-key": KEY! } });
  return (await res.json()) as Profile;
}

const [tiktok, instagram, youtube] = await Promise.all([
  get("tiktok/profile", { handle }),
  get("instagram/profile", { handle }),
  get("youtube/channel", { handle }),
]);

const rows = [tiktok, instagram, youtube]
  .filter((p) => p.success && p.data)
  .map((p) => ({
    platform: p.platform,
    followers: p.data!.author.followers,
    engagement_rate: p.data!.computed.engagement_rate,
    estimated_reach: p.data!.computed.estimated_reach,
  }))
  .sort(
    (a, b) => (b.engagement_rate ?? 0) - (a.engagement_rate ?? 0),
  );

console.table(rows);

What you get back

// console.table output:
// ┌─────────┬───────────┬───────────┬─────────────────┬──────────────────┐
// │ (index) │ platform  │ followers │ engagement_rate │ estimated_reach  │
// ├─────────┼───────────┼───────────┼─────────────────┼──────────────────┤
// │    0    │ "tiktok"  │ 95000000  │       1         │     9500000      │ // <-- clamped, see warnings
// │    1    │ "youtube" │ 351000000 │     0.0842      │     2957420      │
// │    2    │ "instagram"│ 64200000 │     0.0413      │      265146      │
// └─────────┴───────────┴───────────┴─────────────────┴──────────────────┘

Take it further

  • See Computed fields for the per-platform engagement formulas and what triggers the [0, 1] clamp warning visible on TikTok above.
  • Swap mrbeast for any handle on those three platforms — charlidamelio, khaby.lame, mkbhd.
  • Next: Recipe 5 — Spot a song breaking out applies the same parallel-fetch + composite-score pattern to music.

Recipe 5 — Spot a song breaking out

Build an A&R / music-trend dashboard that detects songs gaining traction across TikTok, Spotify, and SoundCloud before they hit the mainstream charts.

Endpoints used (all standard, 1cr each)

  • GET /v1/tiktok/song/videos — videos using a specific TikTok sound (by clipId)
  • GET /v1/spotify/search — Spotify track / artist / podcast search
  • GET /v1/soundcloud/track — SoundCloud track metadata by URL

Cost per run: 3 credits per song.

The three platforms expose the same artefact (a track) from three different vantage points: TikTok shows you who's making content with it, Spotify shows the canonical track stats, SoundCloud shows the underground signal. A simple composite score fuses all three.

// recipe-5-music-heat.ts
// Calculates a cross-platform "heat score" for one track.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-5-music-heat.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

const BASE = "https://www.socialcrawl.dev/v1";

const tiktokClipId = "7349589214432069122";
const spotifyQuery = "Espresso Sabrina Carpenter";
const soundcloudUrl = "https://soundcloud.com/sabrinacarpenter/espresso";

async function get(path: string, params: Record<string, string>) {
  const url = new URL(`${BASE}/${path}`);
  for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
  const res = await fetch(url, { headers: { "x-api-key": KEY! } });
  return (await res.json()) as { success: boolean; data?: Record<string, unknown> };
}

const [tiktok, spotify, soundcloud] = await Promise.all([
  get("tiktok/song/videos", { clipId: tiktokClipId }),
  get("spotify/search", { query: spotifyQuery }),
  get("soundcloud/track", { url: soundcloudUrl }),
]);

// Pull the three signals — every list endpoint returns { items, total? }
const tiktokVideos = (tiktok.data?.total as number | undefined) ?? 0;
const spotifyPlays =
  ((spotify.data?.items as Array<{ post?: { engagement?: { views?: number } } }>)?.[0]
    ?.post?.engagement?.views) ?? 0;
const soundcloudPlays =
  (((soundcloud.data?.post as { engagement?: { views?: number } } | undefined)
    ?.engagement?.views) ?? 0);

// Weighted composite. Tune the weights for your use case.
const heat =
  Math.log10(tiktokVideos + 1) * 0.5 +
  Math.log10(spotifyPlays + 1) * 0.3 +
  Math.log10(soundcloudPlays + 1) * 0.2;

console.log({
  tiktok_videos: tiktokVideos,
  spotify_plays: spotifyPlays,
  soundcloud_plays: soundcloudPlays,
  heat_score: heat.toFixed(2),
});

What you get back

// Aggregated, post-composite:
{
  "tiktok_videos": 2400000,                    // <-- `data.total` from TikTok song/videos
  "spotify_plays": 1180000000,                 // <-- top track from Spotify search
  "soundcloud_plays": 8200000,                 // <-- track plays on SoundCloud
  "heat_score": "4.91"                         // <-- log-weighted composite
}

Take it further

  • See Endpoint pricing — all three platforms are standard tier (1cr each), so scanning a hundred tracks costs 300cr.
  • Swap tiktokClipId to discover any sound's reach (find clip IDs via /v1/tiktok/song); change query to any track + artist string.
  • Next: Recipe 6 — Hybrid search-then-enrich shows the most powerful composition — universal search feeding into per-platform deep-fetches.

Recipe 6 — Hybrid search-then-enrich

Build a deep-research agent that finds every relevant TikTok and Instagram post about a topic, then pulls the transcripts of all of them, then concatenates them into one corpus ready for LLM analysis.

Endpoints used

  • GET /v1/search/everywhere — universal social search (cost: 20cr flat)
  • GET /v1/tiktok/post/transcript — TikTok transcript (cost: 10cr each)
  • GET /v1/instagram/media/transcript — Instagram transcript (cost: 10cr each)

Cost per run: 120 credits (20cr search + 10 enriched posts × 10cr) — a "deep research" budget that produces a real corpus, not just a feed.

This recipe combines the two big SocialCrawl primitives: the universal endpoint that finds where the conversation is happening and the per-platform endpoints that pull what was actually said. Universal search returns the canonical post URL for every result, which feeds straight into the transcript endpoints — no string-massaging required.

// recipe-6-deep-research.ts
// Finds top posts on a topic, fetches their transcripts, returns the corpus.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-6-deep-research.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

const BASE = "https://www.socialcrawl.dev/v1";
const topic = "ozempic side effects";
const POSTS_PER_SOURCE = 5;

// ── Step 1: Universal search, sync mode, scoped to TikTok + Instagram ─────
async function search(query: string) {
  const url = new URL(`${BASE}/search/everywhere`);
  url.searchParams.set("query", query);
  url.searchParams.set("sources", "tiktok,instagram");
  url.searchParams.set("lookback_days", "30");

  const res = await fetch(url, {
    headers: { "x-api-key": KEY!, accept: "application/json" },
  });
  const json = (await res.json()) as {
    success: boolean;
    data: { items: Array<{ source: string; url: string; title: string }> };
    credits_used: number;
  };
  if (!json.success) throw new Error("search failed");
  return json.data.items;
}

// ── Step 2: Per-platform transcript fan-out ───────────────────────────────
type Platform = "tiktok" | "instagram";
const TRANSCRIPT_PATH: Record<Platform, string> = {
  tiktok: "tiktok/post/transcript",
  instagram: "instagram/media/transcript",
};

// See Recipe 2 — transcript shapes differ per platform. This adapter
// covers the three verified live; extend for the others as needed.
function extractText(platform: Platform, data: Record<string, unknown>): string {
  if (platform === "tiktok") return (data.transcript as string) ?? "";
  if (platform === "instagram") {
    const t = data.transcripts as Array<{ text?: string }> | undefined;
    return t?.[0]?.text ?? "";
  }
  return (data.transcript_only_text as string) ?? "";
}

async function getTranscript(platform: Platform, postUrl: string) {
  const url = new URL(`${BASE}/${TRANSCRIPT_PATH[platform]}`);
  url.searchParams.set("url", postUrl);
  const res = await fetch(url, { headers: { "x-api-key": KEY! } });
  const json = (await res.json()) as {
    success: boolean;
    data?: Record<string, unknown>;
    error?: { message: string };
  };
  if (!json.success) {
    console.warn(`skipped ${postUrl}: ${json.error?.message}`);
    return null;
  }
  return { text: extractText(platform, json.data!) };
}

// ── Step 3: Pipeline ──────────────────────────────────────────────────────
const items = await search(topic);

const tiktoks = items.filter((i) => i.source === "tiktok").slice(0, POSTS_PER_SOURCE);
const instagrams = items
  .filter((i) => i.source === "instagram")
  .slice(0, POSTS_PER_SOURCE);

const transcripts = await Promise.all([
  ...tiktoks.map(async (item) => ({
    title: item.title,
    url: item.url,
    transcript: await getTranscript("tiktok", item.url),
  })),
  ...instagrams.map(async (item) => ({
    title: item.title,
    url: item.url,
    transcript: await getTranscript("instagram", item.url),
  })),
]);

const corpus = transcripts
  .filter((t) => t.transcript !== null && t.transcript.text.length > 0)
  .map((t) => `# ${t.title}\n${t.url}\n\n${t.transcript!.text}`)
  .join("\n\n---\n\n");

console.log(`corpus length: ${corpus.length} chars`);
console.log(corpus.slice(0, 500), "…");

What you get back

// The final `corpus` string after Step 3 — markdown-formatted, ready for an LLM:
// # I tried Ozempic for 90 days — here's what happened
// https://www.tiktok.com/@drmike/video/7314...
//
// So I want to talk about what nobody tells you about Ozempic...
//
// ---
//
// # The Ozempic side effect doctors won't mention
// https://www.instagram.com/reel/CzA1234abcd/
//
// Three weeks in I started experiencing what they call...
//
// ---
//
// (8 more transcripts concatenated)

Take it further

  • See Universal social search for advanced filters (from_date, to_date, exclude) you can layer onto step 1 to narrow the candidate set.
  • Swap sources=tiktok,instagram for sources=youtube,reddit and update TRANSCRIPT_PATH — the same pipeline works for any combination of the seven transcript-capable platforms.
  • Next: feed corpus to your LLM of choice and ask for a structured summary, sentiment breakdown, or claim list — that's where the 120-credit budget pays off.

See also

Recipes | Socialcrawl