SocialCrawl

Music trend detection

Detect songs gaining traction across TikTok, Instagram, and Spotify with a cross-platform heat score. 3 credits per song.

You will build an A&R trend detector that scores songs gaining traction across TikTok, Instagram, and Spotify before they reach the mainstream charts.

Cost per run: 3 credits per song (3 platforms x 1 credit). All three endpoints are standard tier, so scanning a hundred tracks costs 300 credits.

Combine three signals. GET /v1/tiktok/song/videos counts how many videos use the sound, GET /v1/instagram/audio/reels shows whether the same audio is spreading on Reels, and GET /v1/spotify/search returns the canonical track stats. A log-weighted composite of the three catches breakouts while chart positions still lag.

By the time a song appears on a chart, the trend is priced in. The early signal lives on the creation side before it shows up on the consumption side: TikTok creation velocity spikes first, Instagram Reels tells you whether the sound crossed platforms or stayed a single-app meme, and Spotify streams confirm the move afterwards.

What you need

Three standard-tier endpoints (1 credit each), one composite score:

EndpointSignalParam
GET /v1/tiktok/song/videosVideos using a specific TikTok soundclipId
GET /v1/instagram/audio/reelsReels using a specific Instagram audio trackaudio_id
GET /v1/spotify/searchSpotify track / artist / podcast searchquery

Resolving the two ids

TikTok and Instagram key off ids, not track names, and both are stable per track. Resolve once, then cache them:

  • TikTok clipId: GET /v1/tiktok/song (1 credit) takes a clipId and returns the sound's metadata, so use it to confirm an id you already scraped from a tiktok.com/music/... URL.
  • Instagram audio_id: the numeric id in an instagram.com/reels/audio/{audio_id}/ URL. GET /v1/instagram/search/music (5 credits, advanced tier) searches Instagram's audio library by keyword and returns matching tracks with their ids.

Because both ids are stable, the 5-credit Instagram lookup is a one-off per track, not a per-run cost. The 3-credit figure above is the steady-state scan.

The code

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

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

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

// Both ids are stable per track, resolve them once and store them.
const tiktokClipId = "7349589214432069122";
const instagramAudioId = "1392969992841787";
const spotifyQuery = "Espresso Sabrina Carpenter";

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, instagram, spotify] = await Promise.all([
  get("tiktok/song/videos", { clipId: tiktokClipId }),
  get("instagram/audio/reels", { audio_id: instagramAudioId }),
  get("spotify/search", { query: spotifyQuery }),
]);

type PostItem = { post?: { engagement?: { views?: number | null } } };

// TikTok reports a total usage count for the sound.
const tiktokVideos = (tiktok.data?.total as number | undefined) ?? 0;

// Instagram returns ONE PAGE of reels, there is no total. Summing views on
// the first page is a reach sample, not a catalogue count. Page with `cursor`
// if you want more depth; the weight below assumes one page.
const instagramReelViews = (
  (instagram.data?.items as PostItem[] | undefined) ?? []
).reduce((sum, item) => sum + (item.post?.engagement?.views ?? 0), 0);

const spotifyPlays =
  (spotify.data?.items as PostItem[] | undefined)?.[0]?.post?.engagement
    ?.views ?? 0;

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

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

What you get back

JSON
// Aggregated, post-composite:
{
  "tiktok_videos": 2400000, // <-- `data.total` from TikTok song/videos
  "instagram_reel_views": 41800000, // <-- summed over ONE page of audio/reels
  "spotify_plays": 1180000000, // <-- top track from Spotify search
  "heat_score": "7.44", // <-- log-weighted composite of the three above
}

What to change

  • The three ids: swap tiktokClipId, instagramAudioId, and spotifyQuery for the track you are watching. Add 5 credits once per track if you still need the /v1/instagram/search/music id lookup.
  • The weights: 0.5 / 0.2 / 0.3 favours TikTok creation velocity. Shift weight to Spotify if you care about confirmed listening rather than early creation.
  • Find candidates to score: GET /v1/instagram/music/trending (5 credits) and GET /v1/instagram/reels/trending (5 credits) surface what is already moving.
  • Deepen the Instagram sample: page audio/reels with cursor so the view sum covers more than the first page, and adjust its weight accordingly.
  • Track the delta, not the level: store heat_score per day. A rising score is the signal; a high static score is a song that already broke.

On this page