TikTok

How to Get TikTok Audience Demographics with an API (2026)

Pull followers, US audience share, engagement rate, and median video views for any public TikTok handle — three GET requests, one research row. Country-level audience demographics included.

Social FetchUpdated

How to Get TikTok Audience Demographics with an API (2026)

Pull followers, US audience share, engagement rate, and median video views for any public TikTok handle — three GET requests, one research row. Country-level audience demographics included.

Creator research usually starts the same way: open a TikTok profile, note the follower count, guess whether the audience is in your market, scroll recent videos, and punch numbers into a spreadsheet. That works for five handles. It falls apart for fifty.

This guide shows how to pull the four numbers most shortlists actually need — followers, US audience %, engagement rate, and median video views — with three Social Fetch requests and one research row per handle.

You'll need an API key and curl or the TypeScript SDK. New here? Start with the Quickstart.

The research brief

MetricWhere it comes from
FollowersGET /v1/tiktok/profiles/{handle}data.metrics.followers
US audience %GET /v1/tiktok/profiles/{handle}/audience → row with countryCode === "US"
Median video viewsGET /v1/tiktok/profiles/{handle}/videos → median of last 20 stats.views
Engagement rateSame videos + followers → avg(likes + comments + shares) ÷ followers × 100

The short version

Three GET requests, one research row. Profile and videos are 1 credit each; audience is 30. Budget about 32 credits per creator on completed lookups.

The audience call is the piece most teams are missing — country-level demographics for a public handle without owning the account. Full reference: Get TikTok profile audience.

Why manual creator research breaks

Hand-rolled shortlists fail in predictable ways:

  • Followers are vanity. A million followers with almost none in your shipping markets is a bad buy for a US-focused brand.
  • Geo is owner-only in TikTok Studio. Third parties cannot open another creator's analytics panel. Guessing from comments or language is noisy.
  • Engagement math drifts. Some tools divide by views, some by followers, some include saves. Without a fixed formula, agencies cannot compare creators.
  • Means lie. One breakout video inflates "average views." Median over a fixed recent window is stabler for vetting.

An API does not remove judgment — it removes the copy-paste loop so your team can apply the same brief to every handle.

Metric 1: Followers

Start with the profile. data.metrics.followers is the denominator for engagement and the size signal on every shortlist.

Request
curl -sS \
  -H "x-api-key: $SOCIALFETCH_API_KEY" \
  "https://api.socialfetch.dev/v1/tiktok/profiles/charlidamelio"
WhatCost
Profile lookup1 credit

Check data.lookupStatus before trusting metrics. found means you have a public profile; private or not_found means skip or flag the row. Details: Get TikTok profile.

Metric 2: US audience share

Audience location is the new piece. Pass the same handle and read country rows under audienceLocations:

Request
const response = await fetch(
  "https://api.socialfetch.dev/v1/tiktok/profiles/charlidamelio/audience",
  {
    headers: {
      "x-api-key": process.env.SOCIALFETCH_API_KEY,
    },
  }
);

const body = await response.json();

console.log(response.status, body);
WhatCost
Audience lookup30 credits

Country-level only

Today this endpoint returns audience countries — not age or gender. Each row has country, countryCode, count, and percentage. Treat percentage as a string (for example "6.00%") and parse it for math. The count field is an upstream sample size, not total followers.

For a US-focused brief, find the row where countryCode is US and parse the percentage:

const usRow = data.audienceLocations?.find((r) => r.countryCode === "US");
const usAudiencePct = usRow ? Number.parseFloat(usRow.percentage) : null;
// usAudiencePct === 6 from "6.00%"

Swap US for any ISO code when the brief targets another market.

Reading the audience response

A trimmed successful response looks like this:

Response
json
{
  "data": {
    "lookupStatus": "found",
    "profile": {
      "platform": "tiktok",
      "handle": "charlidamelio",
      "profileUrl": "https://www.tiktok.com/@charlidamelio"
    },
    "audienceLocations": [
      {
        "country": "United States",
        "countryCode": "US",
        "count": 18,
        "percentage": "6.00%"
      },
      {
        "country": "Brazil",
        "countryCode": "BR",
        "count": 12,
        "percentage": "4.00%"
      }
    ]
  },
  "meta": {
    "requestId": "req_01example",
    "creditsCharged": 30,
    "version": "v1"
  }
}

Fields that matter:

  • data.lookupStatusfound or not_found. HTTP 200 alone is not enough.
  • data.audienceLocations — country rows when found; null when the profile could not be resolved.
  • meta.creditsCharged — 30 on a completed audience lookup.
  • meta.requestId — keep this for support if a row looks wrong.

Metrics 3 and 4: Median views and engagement rate

List recent videos, then derive reach and engagement from stats:

Request
const response = await fetch(
  "https://api.socialfetch.dev/v1/tiktok/profiles/charlidamelio/videos",
  {
    headers: {
      "x-api-key": process.env.SOCIALFETCH_API_KEY,
    },
  }
);

const body = await response.json();

console.log(response.status, body);
WhatCost
Videos page1 credit

Pass sortBy=latest when you want a chronological window (recommended for the median). Each video includes stats.views, likes, comments, and shares. Reference: List TikTok profile videos.

The formulas

Median video views — take the last 20 videos (or fewer if the page is shorter), collect stats.views, sort ascending, take the median. Prefer median over mean so one viral outlier does not dominate the shortlist score.

Engagement rate — for those same videos, average likes + comments + shares, divide by followers, multiply by 100:

engagementRate = (avg(likes + comments + shares) / followers) * 100

Worked example with 3 videos and 100,000 followers:

VideoLikesCommentsSharesSum
A1,20080401,320
B9005030980
C1,500100601,660

Average engagement = (1,320 + 980 + 1,660) / 3 = 1,320.
Engagement rate = 1,320 / 100,000 × 100 = 1.32%.

One script, one research row

Parallel-fetch all three routes and print the four fields:

Example
typescript
import { SocialFetchClient } from "@socialfetch/sdk";

const client = new SocialFetchClient({
  apiKey: process.env.SOCIALFETCH_API_KEY!,
});

const handle = "charlidamelio";
const VIDEO_WINDOW = 20;

const [profileRes, audienceRes, videosRes] = await Promise.all([
  client.tiktok.getProfile({ handle }),
  client.tiktok.getProfileAudience({ handle }),
  client.tiktok.getProfileVideos({ handle, sortBy: "latest" }),
]);

if (!profileRes.ok) {
  console.error(profileRes.error.code, profileRes.error.requestId);
  process.exit(1);
}
if (!audienceRes.ok) {
  console.error(audienceRes.error.code, audienceRes.error.requestId);
  process.exit(1);
}
if (!videosRes.ok) {
  console.error(videosRes.error.code, videosRes.error.requestId);
  process.exit(1);
}

const profile = profileRes.value.data;
const audience = audienceRes.value.data;
const videos = videosRes.value.data.videos;

if (profile.lookupStatus !== "found" || audience.lookupStatus !== "found") {
  console.log({ handle, lookupStatus: profile.lookupStatus });
  process.exit(0);
}

const followers = profile.metrics?.followers ?? 0;

const usRow = audience.audienceLocations?.find(
  (row) => row.countryCode === "US",
);
const usAudiencePct = usRow
  ? Number.parseFloat(usRow.percentage)
  : null;

const window = videos.slice(0, VIDEO_WINDOW);
const views = window.map((v) => v.stats.views).sort((a, b) => a - b);

const medianViews =
  views.length === 0
    ? null
    : views.length % 2 === 1
      ? views[Math.floor(views.length / 2)]!
      : (views[views.length / 2 - 1]! + views[views.length / 2]!) / 2;

const avgEngagement =
  window.length === 0
    ? 0
    : window.reduce((sum, v) => {
        const s = v.stats;
        return sum + s.likes + s.comments + s.shares;
      }, 0) / window.length;

const engagementRate =
  followers > 0 ? (avgEngagement / followers) * 100 : null;

console.log({
  handle,
  followers,
  usAudiencePct,
  medianViews,
  engagementRate,
  creditsCharged:
    profileRes.value.meta.creditsCharged +
    audienceRes.value.meta.creditsCharged +
    videosRes.value.meta.creditsCharged,
});

That is the whole brief: same inputs every time, same formulas, meta.creditsCharged summed so you can budget a roster run.

Batching tips

  • Loop handles, fail soft. Check lookupStatus per creator; write not_found into the sheet instead of aborting the batch.
  • Cache audience. At 30 credits it is the expensive call — re-use it when you only refresh views and engagement weekly.
  • Do not confuse count with followers. Audience count is a sample segment size; size the creator with metrics.followers.
  • Keep the window fixed. Always use the same N (20) so engagement and median views stay comparable across the roster.
  • Preflight private accounts. Videos has no lookupStatus — call profile first when empty lists would mislead a client.

For broader TikTok scraping patterns (pagination, DIY tradeoffs), see How to scrape TikTok data. For multi-network identity before you spend, see Cross-platform creator profiles.

What you can build

  • Influencer shortlist exporter — CSV or Notion row per handle with the four metrics and a geo threshold filter (for example US audience ≥ 40%).
  • Agency pre-brief — auto-fill the research section of a pitch deck before a human reviews creative fit.
  • CRM enrichment — attach followers, US %, median views, and engagement to every TikTok handle in your pipeline.
  • Scheduled re-score — cron the same three calls weekly; flag creators whose US share or engagement drifts past a threshold.

FAQ

How is engagement rate calculated?

Average likes + comments + shares across the last N videos (we use 20), divided by follower count, multiplied by 100. That matches how most creator-research tools compute profile-level engagement. Saves are not included in this formula.

Why median video views instead of the mean?

One viral outlier can pull the average far above what a typical post reaches. The median of the last 20 videos is a stabler estimate of "usual" reach for shortlisting.

Does this include age or gender demographics?

Not yet. The audience endpoint returns country-level location rows only (country, countryCode, count, percentage). Age and gender splits are not in the response today.

How many credits does a full research row cost?

About 32 credits per creator on completed lookups: 1 for profile, 30 for audience, 1 for a page of videos. Audience is the expensive call — cache it when you re-score the same handles often. See the capability matrix.

Is this the same as TikTok Studio audience analytics?

No. TikTok Studio shows the account owner first-party age, gender, and location panels. This API returns third-party country-level audience signals for any public handle you pass — useful for vetting creators you do not control.

Can I use a country other than the US?

Yes. Find the row whose countryCode matches the market you care about (for example BR, GB, or DE) and parse that percentage the same way.


Next steps: Audience endpoint reference · Scrape TikTok profiles & videos · Cross-platform creator profiles · Quickstart · Pricing