Competitor tracking
Snapshot a competitor's follower counts, engagement rates, and latest posts across TikTok, Instagram, YouTube, and X in one parallel sweep — 8 credits per competitor.
Competitor tracking
Build a competitive dashboard that snapshots every competitor's social footprint on a schedule — follower counts, engagement rates, and what they posted this week — so growth spikes and campaign launches show up in your data, not in your boss's Slack message.
How do you track a competitor's social media accounts?
Fetch the competitor's profile and latest posts from each platform's standard endpoints — /v1/tiktok/profile + /v1/tiktok/profile/videos, /v1/instagram/profile + /v1/instagram/profile/posts, /v1/youtube/channel + /v1/youtube/channel/videos, /v1/twitter/profile + /v1/twitter/user/tweets — in one Promise.all. Store each snapshot and diff against the previous run to detect growth and new posts.
The problem
Competitors announce nothing. Their follower spike, their sudden posting streak, the video that's doing 10x their normal numbers — that's the real signal, and it's spread across four platforms with four different APIs, none of which you want to integrate and maintain individually.
The solution
Eight standard-tier endpoints (1 credit each), all behind one key and one envelope:
| Platform | Profile | Latest posts |
|---|---|---|
| TikTok | GET /v1/tiktok/profile (param: handle) | GET /v1/tiktok/profile/videos (param: handle) |
GET /v1/instagram/profile (param: handle) | GET /v1/instagram/profile/posts (param: handle) | |
| YouTube | GET /v1/youtube/channel (param: handle) | GET /v1/youtube/channel/videos (param: handle) |
| X | GET /v1/twitter/profile (param: handle) | GET /v1/twitter/user/tweets (param: handle) |
B2B competitor? Add GET /v1/linkedin/company and GET /v1/linkedin/company/posts (both take the company page url, 1 credit each).
Every profile response carries the same computed.engagement_rate field, so cross-platform comparisons need no per-platform math.
// recipe-competitor-tracking.ts
// Snapshots one competitor across 4 platforms; diff snapshots between runs.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-competitor-tracking.ts
const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");
const BASE = "https://www.socialcrawl.dev/v1";
const competitor = "duolingo"; // same handle on all four platforms here
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;
platform: string;
data?: {
author?: { username: string | null; followers: number | null };
computed?: { engagement_rate: number | null };
items?: Array<{
post?: {
text?: string | null;
created_at?: string | null;
engagement?: { views?: number | null; likes?: number | null };
};
}>;
};
};
}
// ── Step 1: 8 calls in parallel — 4 profiles + 4 post lists ───────────────
const [ttProfile, igProfile, ytProfile, twProfile, ttPosts, igPosts, ytPosts, twPosts] =
await Promise.all([
get("tiktok/profile", { handle: competitor }),
get("instagram/profile", { handle: competitor }),
get("youtube/channel", { handle: competitor }),
get("twitter/profile", { handle: competitor }),
get("tiktok/profile/videos", { handle: competitor }),
get("instagram/profile/posts", { handle: competitor }),
get("youtube/channel/videos", { handle: competitor }),
get("twitter/user/tweets", { handle: competitor }),
]);
// ── Step 2: Build today's snapshot ─────────────────────────────────────────
const posts = [ttPosts, igPosts, ytPosts, twPosts];
const snapshot = {
date: new Date().toISOString().slice(0, 10),
handle: competitor,
platforms: [ttProfile, igProfile, ytProfile, twProfile]
.filter((p) => p.success && p.data)
.map((p, i) => ({
platform: p.platform,
followers: p.data!.author?.followers ?? null,
engagement_rate: p.data!.computed?.engagement_rate ?? null,
posts_fetched: posts[i]?.data?.items?.length ?? 0,
latest_post: posts[i]?.data?.items?.[0]?.post?.text?.slice(0, 80) ?? null,
})),
};
console.table(snapshot.platforms);
// ── Step 3: Diff against your stored snapshot from the previous run ───────
// Persist `snapshot` to a DB table keyed by (handle, date), then:
// follower_delta = today.followers - yesterday.followers
// new_posts = posts with created_at > yesterday's run time
// engagement_jump = today.engagement_rate / trailing 7-day average
// Alert when any delta crosses your threshold.What you get back
// console.table(snapshot.platforms):
// ┌─────────┬─────────────┬───────────┬─────────────────┬───────────────┬──────────────────────────────┐
// │ (index) │ platform │ followers │ engagement_rate │ posts_fetched │ latest_post │
// ├─────────┼─────────────┼───────────┼─────────────────┼───────────────┼──────────────────────────────┤
// │ 0 │ "tiktok" │ 17800000 │ 0.1124 │ 20 │ "the owl has no chill..." │
// │ 1 │ "instagram" │ 4900000 │ 0.0381 │ 12 │ "new feature drop 🦉" │
// │ 2 │ "youtube" │ 8120000 │ 0.0512 │ 30 │ "How Duo learned Spanish" │
// │ 3 │ "twitter" │ 16200000 │ null │ 100 │ "spanish or vanish" │
// └─────────┴─────────────┴───────────┴─────────────────┴───────────────┴──────────────────────────────┘Credits cost
Cost per run: 8 credits per competitor (4 profiles + 4 post lists × 1 credit). Tracking 10 competitors daily costs 2,400 credits/month. Adding LinkedIn company + posts brings it to 10 credits per competitor.
Take it further
- Rank all competitors with comparable rates using Creator engagement scoring — the same
computed.engagement_ratepowers both recipes. - Catch competitor mentions (not just their own posts) with Brand mention monitoring, and their paid side with Ad library aggregation.
- Handles differ per platform in reality — keep a
{ tiktok, instagram, youtube, twitter }handle map per competitor instead of one string. - Platform references: TikTok API, Instagram API, YouTube API, Twitter API. New here? Quickstart.
