SocialCrawl
Recipes

Audience demographics

Estimate who a creator or video actually reaches. Get a TikTok creator's audience country breakdown in one call, then build a sampled age and gender estimate for any specific video from its commenters.

Audience demographics

Build the audience slide of an influencer-vetting or media-planning report: where a creator's audience lives, and the approximate age and gender mix of the people engaging with a specific video.

How do you get audience demographics with an API?

Platforms only show viewer demographics (age, gender, location of the people who watched) to the account owner, inside TikTok Analytics, YouTube Studio, and Meta Insights. That data never reaches any public surface, so no third-party API can return it for arbitrary public videos. Vendors that advertise it are estimating. SocialCrawl gives you two building blocks that estimate it honestly: /v1/tiktok/user/audience returns a TikTok creator's audience country breakdown natively, and /v1/utility/age-gender turns a sample of a video's commenters into an age and gender estimate on any platform with a comments endpoint.

The problem

You are shortlisting creators for a campaign that targets women aged 18 to 30 in the US and Mexico. Every creator's media kit claims their audience matches. The platforms will not tell you: viewer demographics are locked to the account owner's own dashboard, and screenshots of those dashboards are easy to fake and impossible to refresh.

The solution

Two endpoints, composable with any comments endpoint:

  • GET /v1/tiktok/user/audience (5 credits) — the creator-level answer. Returns audienceLocations: countries ranked by share of a sample of roughly 1,000 audience accounts, with counts and percentages.
  • GET /v1/utility/age-gender (10 credits) — the per-video building block. Takes any social profile URL and returns an estimated age range and gender (with confidence) from the profile's avatar.
  • GET /v1/tiktok/post/comments, /v1/youtube/video/comments, /v1/facebook/post/comments (1 credit each) — the sampling frame: real accounts that engaged with the specific video.

Chain them: pull a video's commenters, run age-gender detection on each commenter's profile, aggregate. The result is a demographic estimate of the video's active audience.

// recipe-audience-demographics.ts
// Audience geography for a creator + sampled age/gender for one of their videos.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-audience-demographics.ts

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

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

async function get<T>(path: string, params: Record<string, string>): Promise<T> {
  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 T;
}

// 1. Which countries is the creator's audience in? (5 credits)
type AudienceRes = {
  data?: {
    audienceLocations: Array<{
      country: string;
      countryCode: string;
      count: number;
      percentage: string;
    }>;
  };
};
const audience = await get<AudienceRes>("tiktok/user/audience", {
  handle: "charlidamelio",
});
console.table(audience.data?.audienceLocations.slice(0, 5));

// 2. Who engages with this specific video? Sample its commenters. (1 credit)
type CommentsRes = {
  data?: { items: Array<{ author: { username: string | null } }> };
};
const comments = await get<CommentsRes>("tiktok/post/comments", {
  url: "https://www.tiktok.com/@charlidamelio/video/7667257618544495886",
});
const usernames = (comments.data?.items ?? [])
  .map((c) => c.author.username)
  .filter((u): u is string => Boolean(u))
  .slice(0, SAMPLE_SIZE);

// 3. Estimate age + gender per commenter from their profile avatar. (10 credits each)
type AgeGenderRes = {
  data?: {
    ageRange: { low: number; high: number };
    gender: string;
    confidence: { gender: number };
  };
};
const sampled = await Promise.all(
  usernames.map(async (username) => {
    const r = await get<AgeGenderRes>("utility/age-gender", {
      url: `https://www.tiktok.com/@${username}`,
    });
    return r.data
      ? { username, age: `${r.data.ageRange.low}-${r.data.ageRange.high}`, gender: r.data.gender }
      : null;
  }),
);
const rows = sampled.filter((r) => r !== null);
console.table(rows);

// 4. Aggregate the sample into a demographic estimate.
const genderShare = rows.reduce<Record<string, number>>((acc, r) => {
  acc[r.gender] = (acc[r.gender] ?? 0) + 1;
  return acc;
}, {});
console.log("Gender mix of sampled commenters:", genderShare);

For YouTube, swap step 2 for youtube/video/comments and build profile URLs as https://www.youtube.com/@${username}; for Facebook, use facebook/post/comments. The age-gender endpoint accepts profile URLs from any platform.

What you get back

// Step 1 — audienceLocations (top 5 of ~60 countries):
// ┌─────────┬─────────────────┬─────────────┬───────┬────────────┐
// │ (index) │ country         │ countryCode │ count │ percentage │
// ├─────────┼─────────────────┼─────────────┼───────┼────────────┤
// │    0    │ "United States" │ "US"        │  185  │ "19.49%"   │
// │    1    │ "Mexico"        │ "MX"        │  156  │ "16.44%"   │
// │    2    │ "Pakistan"      │ "PK"        │  70   │ "7.38%"    │
// │    3    │ "Argentina"     │ "AR"        │  51   │ "5.37%"    │
// │    4    │ "Colombia"      │ "CO"        │  47   │ "4.95%"    │
// └─────────┴─────────────────┴─────────────┴───────┴────────────┘

// Step 3 — sampled commenters:
// ┌─────────┬──────────────────┬─────────┬──────────┐
// │ (index) │ username         │ age     │ gender   │
// ├─────────┼──────────────────┼─────────┼──────────┤
// │    0    │ "dandefleurette" │ "23-29" │ "Female" │
// │   ...   │                  │         │          │
// └─────────┴──────────────────┴─────────┴──────────┘

Know the limits

  • Commenters are not viewers. The sample describes the video's active audience, which skews toward engaged fans. Treat it as a directional signal, not a viewer census.
  • Avatar-based estimates. Age and gender come from the profile picture. Accounts with logos, pets, or cartoon avatars return low-confidence or missing estimates; filter on confidence.gender if you need precision.
  • Sample percentages are directional. audienceLocations is drawn from roughly 1,000 audience accounts. A 19.5% share means "about a fifth", not a two-decimal truth.

Credits cost

Cost per run: 106 credits (5 for the creator geography + 1 for comments + 10 commenters × 10 credits). Widen the age/gender sample to 30 commenters for 306 credits per video.

Take it further

  • Prefer one call per video? /v1/prism/video-intel returns detail, stats, transcript, top comments, and a commenter sample for YouTube, TikTok, Rumble, and Instagram in a single metered request.
  • Compare two TikTok creators' audiences directly with /v1/prism/audience-overlap: Jaccard similarity plus a shared-fan count.
  • Pair with Creator engagement scoring to vet the same shortlist on performance, not just demographics.
  • Want what the audience thinks, not just who they are? Sentiment analysis runs the same comment surfaces through an LLM.