Home > Blog > How to Get TikTok Sound and Music Info via API
Music

How to Get TikTok Sound and Music Info via API

By PrimeApi·August 15, 2026·8 min read·Updated August 15, 2026

A TikTok sound is a first-class object on the platform. It has its own ID, its own page and its own catalogue of videos filmed over it. Most tooling ignores that and treats audio as a caption-level string, which is why so many trending-sound dashboards cannot tell you how long a track runs, who it is attributed to, or whether it is a licensed release or something a creator hummed into a phone. This guide covers the two calls that fix that: music-info, which resolves a numeric music ID into the full sound record, and music-posts, which lists the videos built on it. Every field named below comes from live responses captured against the production API.

Where a musicId comes from

Neither endpoint accepts a sound name, a slug or a search term. Both require musicId, the numeric identifier TikTok assigns to a sound - something like 7224128604890990593. There are two ways to obtain one, and only one of them is worth building a pipeline on.

Out of any video record

This is the reliable route. Every video object the API returns carries a nested music object, and that object's id is the musicId. In post-detail it sits at itemInfo.itemStruct.music.id. On the feed-shaped endpoints - user-posts, user-liked-posts, user-repost, challenge-posts, place-posts - every entry in itemList exposes the same music.id. So any crawl you are already running is quietly collecting sound IDs, and the only work left is to deduplicate them.

That nested block is not empty either. On the feed endpoints it carries id, title, authorName, playUrl, the three cover sizes, duration, shoot_duration, original, private, isCopyrighted, is_commerce_music, is_unlimited_music and tt2dsp. The copy inside post-detail adds album, preciseDuration, collected and scheduleSearchTime. If all you need is a label for a video, use what you already have and skip the extra call. Two things send you to music-info anyway: the artist profile, and the platform-wide video count for the sound - neither of which appears in the nested copy. The walkthrough on reading TikTok video data via API covers that item structure in full.

Out of a sound page URL

Public TikTok sound pages end in the numeric music ID, so pasting a link and taking the trailing digits works for one-off lookups and for accepting user input in a tool. Treat it as a convenience, not infrastructure: URL formats change, slugs get rewritten, and a video record hands you the same number without any parsing.

Calling music-info

The base URL is https://api.primeapi.co/ and authentication is one header, X-PrimeAPI-Key. music-info takes a single required parameter, musicId, and has no optional ones - there is nothing to page, because a sound record is one object.

curl

curl -s "https://api.primeapi.co/music-info?musicId=7224128604890990593" \
  -H "X-PrimeAPI-Key: YOUR_API_KEY"

Node.js (Axios)

const axios = require("axios");

const KEY = "YOUR_API_KEY";

async function getSound(musicId) {
  const res = await axios.get("https://api.primeapi.co/music-info", {
    params: { musicId },
    headers: { "X-PrimeAPI-Key": KEY }
  });

  const info = res.data.musicInfo || {};
  const m = info.music || {};

  console.log("balance:", res.headers["x-primeapi-balance"]);

  return {
    id: m.id,
    title: m.title,
    author: m.authorName,
    album: m.album,
    seconds: m.duration,
    play: m.playUrl,
    cover: m.coverLarge,
    isOriginalSound: m.original === true,
    licensed: m.isCopyrighted === true,
    artistHandle: info.artist ? info.artist.uniqueId : null,
    videoCount: info.stats ? info.stats.videoCount : null
  };
}

getSound("7224128604890990593")
  .then(s => console.log(s))
  .catch(err => console.error(err.response ? err.response.data : err.message));

Python (Requests)

import requests

BASE = "https://api.primeapi.co"
HEADERS = {"X-PrimeAPI-Key": "YOUR_API_KEY"}

def get_sound(music_id):
    r = requests.get(
        f"{BASE}/music-info",
        params={"musicId": music_id},
        headers=HEADERS,
        timeout=15,
    )
    r.raise_for_status()

    info = r.json().get("musicInfo", {})
    music = info.get("music", {})
    artist = info.get("artist") or {}

    return {
        "id": music.get("id"),
        "title": music.get("title"),
        "author": music.get("authorName"),
        "album": music.get("album"),
        "seconds": music.get("duration"),
        "shoot_seconds": music.get("shoot_duration"),
        "play_url": music.get("playUrl"),
        "original": music.get("original"),
        "copyrighted": music.get("isCopyrighted"),
        "commerce": music.get("is_commerce_music"),
        "artist_handle": artist.get("uniqueId"),
        "artist_name": artist.get("nickname"),
        "video_count": info.get("stats", {}).get("videoCount"),
        "dsp": bool(music.get("tt2dsp", {}).get("tt_to_dsp_song_infos")),
    }

if __name__ == "__main__":
    print(get_sound("7224128604890990593"))

The sound record, field by field

Everything useful lives under musicInfo, which holds four children: music, artist, artists and stats. The track itself is musicInfo.music.

FieldTypeWhat it holds
idstringThe musicId you passed in. Your primary key.
titlestringThe sound name as TikTok displays it.
authorNamestringThe credited author string on the sound.
albumstringAlbum name for catalogue releases.
durationintLength of the sound in seconds.
shoot_durationintThe second duration value TikTok exposes, for the recordable segment.
playUrlstringDirect CDN link to the audio.
coverThumb / coverMedium / coverLargestringArtwork at three sizes.
originalboolTrue for a creator-recorded original sound.
privateboolTrue when the sound is not openly available.
isCopyrightedboolRights flag for catalogue material.
is_commerce_musicboolWhether the track sits in the commercial-use library.
is_unlimited_musicboolCompanion usage flag on the same record.
tt2dspobjectHolds tt_to_dsp_song_infos, the streaming-service mapping.

The artist block is an account, not a label

musicInfo.artist is a compact profile: id, uniqueId, nickname, secUid, signature, the three avatar sizes, and the flags privateAccount, secret, ftc, openFavorite and relation. musicInfo.artists is an array of the same shape, so read the array when you need every credited account and keep artist as the singular fallback.

Be careful what you infer from it. This is the TikTok account attached to the sound record, which is not always the official artist account - in one response captured for this guide the nickname read as the recording artist while uniqueId was an unrelated personal handle. Display nickname if you like, but do not build attribution logic on uniqueId without checking it. Note also that the artist block carries no verified field; if you need that, resolve the handle separately with userinfo-by-username, which returns the full profile including the verification flag.

Reach, share metadata and the envelope

musicInfo.stats contains exactly one number: videoCount, the platform-wide count of videos using this sound. That single integer is the cheapest audio-trend metric there is - one credit, one field, and sampled daily it gives you a growth curve per sound without touching a single video. Outside musicInfo the response carries shareMeta with title and desc - the pre-formatted share strings - plus statusCode, status_code, status_msg, an extra object with logid and now, and log_pb.impr_id. Log those two identifiers; they are the fastest way to point support at one specific upstream response.

Original sounds versus licensed tracks

Four booleans decide how you should treat a sound. original true means a creator made it - a voiceover, a skit, a remix recorded in the app. Those sounds have no album, usually nothing under tt2dsp, and an artist block that is simply the creator's own account. isCopyrighted, is_commerce_music and is_unlimited_music describe the rights side of catalogue material, which matters if you are advising brands on what they can legally use. The distinction also shapes analysis: an original sound spreading across thousands of videos is an organic meme, while a licensed track doing the same is usually a label campaign.

playUrl, and when to use download-music instead

playUrl is a link, not a file. Fetch it yourself if you want the audio, and expect CDN links to expire, so store the bytes rather than the URL if you are archiving. When your input is a video URL rather than a music ID, download-music is the shorter path - it returns a single play field and nothing else, which makes it cheap to run in bulk. The post on downloading TikTok audio as MP3 covers the difference in detail.

From the sound to its videos: music-posts

music-posts takes the same musicId plus optional count and cursor. Before you write the parser, note that its envelope is nothing like music-info. The payload is wrapped in code, msg, processed_time and data; the items live in data.videos; and cursor and hasMore both come back as integers, so hasMore is 1 or 0 rather than true or false.

def get_sound_videos(music_id, pages=4, page_size=20):
    videos, cursor = [], 0
    for _ in range(pages):
        r = requests.get(
            f"{BASE}/music-posts",
            params={"musicId": music_id, "count": page_size, "cursor": cursor},
            headers=HEADERS,
            timeout=20,
        )
        r.raise_for_status()
        data = r.json().get("data", {})

        videos.extend(data.get("videos", []))
        cursor = data.get("cursor", 0)
        if not data.get("hasMore"):
            break

    return videos

for v in get_sound_videos("7224128604890990593"):
    print(v["aweme_id"], v["play_count"], v["title"][:60])

Each video in data.videos is flat rather than nested. Identity comes from aweme_id and video_id, with region, title, content_desc, duration and create_time describing the clip. Imagery is cover, origin_cover and ai_dynamic_cover. The playable files are play and the watermarked wmplay, with size and wm_size for each. Engagement arrives as play_count, digg_count, comment_count, share_count, download_count and collect_count, and each item repeats its own music object. The remaining fields - anchors, anchors_extras, is_ad, commerce_info, commercial_video_info, item_comment_settings and mentioned_users - are mostly commerce and moderation metadata, and is_ad is the one worth filtering on before you compute averages.

Two guides go further with this endpoint: one on finding every video using a TikTok sound, and one on tracking trending TikTok sounds, which builds on the videoCount sampling described above.

Credits, limits and errors

Each request costs 1 credit whatever it returns, and the balance left comes back in X-PrimeAPI-Balance. The default rate limit is 100 requests per minute, and responses are live with no caching, averaging about a second. A sound profile is one call, so a catalogue of five thousand tracks costs five thousand credits and under an hour of wall clock; the video pages are the expensive half, so budget by pages of music-posts rather than by sounds. New accounts get 50 free credits, which is enough to test both endpoints against a real track before writing anything. Paid tiers on the pricing page run from 2,500 credits ($9.90) to 500,000 ($279.00).

Four failures arrive as a JSON message rather than a bare status code: "Please sign up to primeapi.co" when the key header is missing, "PrimeAPI-Key is not available" for a wrong or unactivated key, the per-minute limit message when you cross 100 requests in a window, and "Your balance has been exhausted..." when credits run out. Branch on that body. Separately, a deleted or restricted sound will answer without the data you expected rather than with an error, so check that musicInfo.music.id exists before you read from it. Full parameter tables live in the API documentation, and both calls can be run against a live sound in the playground before you write any code.

Putting it together

The working pattern is short. Harvest music.id from the video records you are already collecting, deduplicate, then call music-info once per sound and store title, authorName, duration, original, the rights flags and stats.videoCount. Re-read videoCount on a schedule and the delta tells you which sounds are accelerating; only then spend credits on music-posts to see who is actually using them. That order matters, because it puts one cheap call in front of the expensive paging and keeps a sound catalogue affordable at scale. PrimeApi is an independent service and is not affiliated with or endorsed by TikTok or ByteDance; create an account and the free credits will cover a first pass over a few dozen tracks.

Frequently asked questions

Where do I get a TikTok musicId?

From any video record. Every item the API returns carries a nested music object, and its id is the musicId - in post-detail it sits at itemInfo.itemStruct.music.id, and on feed endpoints such as user-posts, challenge-posts or place-posts it is music.id on each entry of itemList. A public sound page URL also ends in that same number, but reading it out of a video record is the version you can automate.

Does music-info return the audio file itself?

It returns playUrl, a direct CDN link to the sound, plus coverThumb, coverMedium and coverLarge for the artwork. That is a link, not a file - you still fetch it yourself. If you are starting from a video URL rather than a music ID, download-music is the shorter path, since it answers with a single play field.

What does the original flag mean?

original is a boolean on the music object that separates a sound a creator recorded themselves from a track that came out of TikTok's licensed catalogue. Read it together with isCopyrighted, is_commerce_music and is_unlimited_music, which describe the rights side, and with private, which tells you the sound is not openly available.

Where are the streaming service links?

Inside tt2dsp, which holds a single key, tt_to_dsp_song_infos. That is where the mapping from a TikTok sound to catalogue entries on streaming services lives. It only makes sense for released music, so for creator-recorded original sounds there is nothing to map - check it exists before you index into it.

Why does music-posts look nothing like music-info?

They come from different upstream shapes and you should not share a parser. music-info nests everything under musicInfo and pages nothing. music-posts wraps its payload in code, msg, processed_time and data, puts the items in data.videos, and returns cursor and hasMore as integers rather than a string and a boolean.

How many credits does a sound lookup cost?

One credit per request, the same as every endpoint, with the remaining balance in the X-PrimeAPI-Balance response header. A sound record is a single call, so profiling a thousand tracks costs a thousand credits; the video pages you pull afterwards with music-posts are what actually drive spend. New accounts start with 50 free credits.

Start building with the PrimeApi TikTok API
50 free credits when you sign up. No card required - you only pay when you need more.

← Back to the PrimeApi Blog