The TikTok Music API Cookbook: 12 Recipes for Sound Insight

Published on May 29, 2026

Music supervisors, A&R analysts, label data teams, and rights-management platforms all share one problem: TikTok is now the dominant discovery engine for music, but its data is locked behind an opaque app. A track can go from 200 plays to two million in 72 hours, a sync licensing opportunity can appear and disappear in a weekend, and a regional breakout can quietly dictate next quarter's tour routing - all without anyone at a label noticing until the chart shows up.

This cookbook gives you 12 production-ready recipes for extracting music intelligence from TikTok using the TikLiveAPI. Each recipe is a copy-paste Python snippet built around three endpoints: /music-info/ for sound metadata, /music-posts/ for the videos using a sound, and /search-video/ for keyword and lyric discovery. Authentication is the same everywhere - send your key in the X-Api-Key header. Grab one from the register page (100 free credits on email verification) and try requests live in the playground before wiring anything up.

One credit equals one request. No subscriptions, no expiry, no negotiation. The pricing page has the full breakdown, and the documentation covers all 37 endpoints. The two you will live in for music work are music endpoints and posts endpoints.

Setup: one client, every recipe

Every recipe below assumes this minimal client. Drop it in a file called tla.py and import it.

import os, time, requests

BASE = "https://api.tikliveapi.com"
KEY  = os.environ["TLA_KEY"]
HEADERS = {"X-Api-Key": KEY}

def get(path, **params):
    r = requests.get(f"{BASE}{path}", headers=HEADERS, params=params, timeout=30)
    r.raise_for_status()
    return r.json()

That is the entire SDK. No retries, no caching, no abstraction - you can add those once you know what you actually need.

Recipe 1: Resolve a sound from a TikTok URL

You almost never start with a raw music_id. You start with a TikTok video URL someone slacked you. Resolve it first via /post-detail/, then pivot to the music object.

from tla import get

def resolve_music(video_url):
    post = get("/post-detail/", url=video_url)
    music = post.get("music_info") or post.get("music") or {}
    music_id = post.get("music_info", {}).get("id")
    return music_id, music

music_id, meta = resolve_music("https://www.tiktok.com/@user/video/7300000000000000000")
print(music_id)

Cost: 1 credit per video resolved. Cache the mapping - URLs are stable.

Recipe 2: Get canonical sound metadata

Once you have a music_id, /music-info/ returns the canonical record: title, author (the credited artist or the username for originals), cover image, duration, and the original-sound flag.

def music_meta(music_id):
    data = get("/music-info/", music_id=music_id)
    return {
        "id": music_id,
        "title": data.get("title"),
        "author": data.get("author"),
        "duration": data.get("duration"),
        "is_original": bool(data.get("original")),
        "cover": data.get("cover"),
    }

print(music_meta("7012345678901234567"))

This is your normalization layer. Always hit /music-info/ once per new sound and write it to your warehouse - downstream joins all key off music_id.

Recipe 3: Fetch the first page of posts using a sound

The growth story lives in /music-posts/. The first page gives you the most recent uses - perfect for "is this still moving?" checks.

def music_posts_page(music_id, count=30, cursor=0):
    return get("/music-posts/", music_id=music_id, count=count, cursor=cursor)

page = music_posts_page("7012345678901234567", count=30)
for v in page.get("videos", []) or page.get("videos", []):
    print(v.get("aweme_id"), v.get("play_count"), v.get("digg_count"))

Field names in post lists are snake_case (aweme_id, play_count, digg_count). Memorize that - the user endpoints mix camelCase and it trips everyone up once.

Recipe 4: Paginate the full history of a sound

To compute velocity or share of voice you need more than the first page. /music-posts/ paginates via cursor + hasMore. Keep a sleep in there - you are not in a race.

def all_music_posts(music_id, max_pages=20, page_size=30):
    cursor, out = 0, []
    for _ in range(max_pages):
        page = get("/music-posts/", music_id=music_id, count=page_size, cursor=cursor)
        items = page.get("videos") or page.get("videos") or []
        out.extend(items)
        if not page.get("hasMore"):
            break
        cursor = page.get("cursor", cursor + page_size)
        time.sleep(0.25)
    return out

posts = all_music_posts("7012345678901234567", max_pages=10)
print(len(posts), "posts collected")

Cost: max_pages credits. Cap it. A track with 800k uses will happily drain your balance if you let it.

Recipe 5: Weekly velocity for an A&R watchlist

Velocity is the gradient that matters. Bucket the posts by week using create_time (Unix seconds) and you instantly see whether a sound is accelerating, plateauing, or dying.

from collections import Counter
from datetime import datetime, timezone

def weekly_velocity(music_id, pages=10):
    posts = all_music_posts(music_id, max_pages=pages)
    weeks = Counter()
    for p in posts:
        ts = p.get("create_time") or 0
        if not ts: continue
        wk = datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%G-W%V")
        weeks[wk] += 1
    return dict(sorted(weeks.items()))

print(weekly_velocity("7012345678901234567"))

Run this nightly across your watchlist. A jump from 40 posts/week to 400 posts/week is the signal that gets a track into Monday's pitch meeting.

Recipe 6: Detect viral-rising sounds before the charts

Sounds do not go viral linearly - they bend. Compare the most recent 7 days against the prior 7. A 3x ratio with a meaningful floor (say, >= 100 posts in the recent window) is the classic breakout signature.

def rising_score(music_id):
    posts = all_music_posts(music_id, max_pages=15)
    now = int(time.time())
    recent = sum(1 for p in posts if now - (p.get("create_time") or 0) < 7*86400)
    prior  = sum(1 for p in posts if 7*86400 <= now - (p.get("create_time") or 0) < 14*86400)
    if prior == 0:
        return recent, prior, float("inf") if recent else 0
    return recent, prior, round(recent / prior, 2)

print(rising_score("7012345678901234567"))

Wrap this in a loop over your watchlist and flag anything with a ratio above 3 and recent count above 100. That is your daily "what is breaking" report.

Recipe 7: Find the dominant region for a sound

Knowing a track is hot is not enough - you need to know where. Each post item carries author region data; the modal region is your best cheap proxy until you spend credits on dedicated region endpoints.

def top_region(music_id):
    posts = all_music_posts(music_id, max_pages=10)
    regions = Counter()
    for p in posts:
        region = p.get("region") or p.get("region")
        if region:
            regions[region] += 1
    return regions.most_common(5)

print(top_region("7012345678901234567"))

If a US-signed artist's track is 70% Indonesian, that is a tour-routing conversation. If it is 90% Brazilian and the artist has no PT-BR promo, that is a marketing brief.

Recipe 8: Original sound vs cover - the attribution question

For royalty work the difference between an original upload and a labelled cover is everything. The original flag on /music-info/ is your first filter; the author field tells you who TikTok credits.

def attribution(music_id):
    meta = get("/music-info/", music_id=music_id)
    return {
        "music_id": music_id,
        "title": meta.get("title"),
        "credited_author": meta.get("author"),
        "is_original_upload": bool(meta.get("original")),
        "needs_manual_review": bool(meta.get("original")) and not meta.get("album"),
    }

print(attribution("7012345678901234567"))

The needs_manual_review flag is the one your rights team will care about - an "original" upload with no album metadata is exactly the kind of orphaned IP that ends up in disputes.

Recipe 9: Share of voice across a catalog

You manage 40 tracks. Which one is actually generating TikTok activity this week? Run the velocity recipe over your catalog and rank.

def catalog_share_of_voice(music_ids):
    rows = []
    for mid in music_ids:
        recent, prior, ratio = rising_score(mid)
        rows.append({"music_id": mid, "recent7d": recent, "prior7d": prior, "ratio": ratio})
    total = sum(r["recent7d"] for r in rows) or 1
    for r in rows:
        r["share"] = round(r["recent7d"] / total * 100, 1)
    return sorted(rows, key=lambda r: -r["share"])

catalog = ["7012345678901234567", "7012345678901234568", "7012345678901234569"]
for row in catalog_share_of_voice(catalog):
    print(row)

Cost: roughly 15 credits per track. Forty tracks = 600 credits weekly. Cheap insurance against missing your own breakout.

Recipe 10: Sync licensing lead discovery

Brand sync teams want videos using a sound that already have brand momentum. Combine /search-video/ with a sound filter check - find videos mentioning a brand keyword, then keep only those using a target sound.

def sync_leads(keyword, target_music_id, pages=3):
    leads, cursor = [], 0
    for _ in range(pages):
        page = get("/search-video/", keyword=keyword, count=20, cursor=cursor)
        items = page.get("videos") or page.get("videos") or []
        for v in items:
            mid = (v.get("music_info") or {}).get("id") or v.get("music_id")
            if str(mid) == str(target_music_id):
                leads.append({
                    "aweme_id": v.get("aweme_id"),
                    "plays": v.get("play_count"),
                    "author": (v.get("author") or {}).get("unique_id"),
                })
        if not page.get("hasMore"): break
        cursor = page.get("cursor", cursor + 20)
        time.sleep(0.25)
    return leads

print(sync_leads("nike", "7012345678901234567"))

Output is a ranked list of creators already pairing the brand and the sound - that is your outbound sheet for the licensing conversation.

Recipe 11: Daily snapshot job

Insight compounds when you snapshot daily. Persist the watchlist metrics to a single JSONL file and you have a longitudinal dataset within a month.

import json
from datetime import date

def daily_snapshot(music_ids, path="snapshots.jsonl"):
    today = date.today().isoformat()
    with open(path, "a") as f:
        for mid in music_ids:
            meta = music_meta(mid)
            recent, prior, ratio = rising_score(mid)
            f.write(json.dumps({
                "date": today, "music_id": mid, "title": meta["title"],
                "author": meta["author"], "recent7d": recent,
                "prior7d": prior, "ratio": ratio,
            }) + "\n")

daily_snapshot(["7012345678901234567", "7012345678901234568"])

Schedule via cron at 04:00 local. Within four weeks you can backfit weekly seasonality and stop chasing weekend noise as if it were a trend.

Recipe 12: Network graph of co-occurring sounds

Creators who use sound A often use sound B. That co-occurrence graph is how you find adjacent breakout candidates - tracks that share an audience but have not popped yet.

from collections import defaultdict

def cooccurrence(seed_music_id, depth_pages=5):
    posts = all_music_posts(seed_music_id, max_pages=depth_pages)
    authors = {(p.get("author") or {}).get("unique_id") for p in posts}
    authors.discard(None)
    graph = defaultdict(int)
    for handle in list(authors)[:25]:
        page = get("/search-video/", keyword=handle, count=10)
        for v in page.get("videos", []) or page.get("videos", []):
            mid = (v.get("music_info") or {}).get("id") or v.get("music_id")
            if mid and str(mid) != str(seed_music_id):
                graph[mid] += 1
        time.sleep(0.25)
    return sorted(graph.items(), key=lambda x: -x[1])[:20]

print(cooccurrence("7012345678901234567"))

The top-ranked co-occurring sounds are your next watchlist entries. Loop in the music endpoints to enrich each one with metadata.

Cost projection

Plan credits like you plan compute. Rough numbers for a label running a 50-track watchlist:

  • Daily snapshot (Recipe 11): 50 tracks x 16 credits = 800 credits/day = 24,000 credits/month.
  • Weekly co-occurrence (Recipe 12): 50 tracks x 30 credits = 1,500 credits/week = 6,000 credits/month.
  • Sync lead scans (Recipe 10): 20 keyword x 3 pages weekly = 240 credits/month.
  • Ad-hoc resolution + metadata: budget 1,000 credits/month.

Total: roughly 31,000 credits/month for a serious A&R + sync operation. Credits never expire, so under-using one month banks budget for a release window the next. Check current bundles on the pricing page.

FAQ

Do I need to send anything besides the API key?

No. Every endpoint authenticates with a single X-Api-Key header. No OAuth, no signing, no rotating tokens.

Why do some endpoints use camelCase and others snake_case?

The response shape mirrors TikTok's internal schemas. /music-info/ and the post lists use snake_case; user info endpoints use camelCase. Normalize on ingest and you will never think about it again.

How do I get a music_id from a TikTok URL I have?

Resolve via /post-detail/ as in Recipe 1. The music_info.id field is your music_id for every other call.

Can I download the audio?

Yes. /post-detail/ returns a direct music mp3 URL alongside watermark-free video links (play, hdplay). Respect rights holders - downloads are for analysis, not redistribution.

What is the difference between /music-posts/ and /search-video/?

/music-posts/ is sound-anchored - give it a music_id and get every video using that exact sound. /search-video/ is keyword-anchored - give it text and get videos whose captions, hashtags, or sound titles match. Use the former for tracking, the latter for discovery.

How fresh is the data?

Responses are pulled in real time from TikTok with millisecond-level latency on cached metadata. Treat counts as accurate to the minute, not the second.

Where can I try these without writing code?

The playground runs every endpoint in the browser with your key, and the documentation shows request and response schemas side by side. Start there before you wire anything into your warehouse.

That is the cookbook. Twelve recipes, three endpoints, one header. Ship the daily snapshot job first - it will pay for itself the first week you catch a breakout your competitors miss.

Build with the TikTok API

Ready to put what you read into code? Try our endpoints live or grab the full reference.

Open Playground Read Documentation