Guide · Python

Track every TikTok video that uses a sound with Python

Give the script a sound ID and it pages through every video made with that sound, then writes the creators, views, likes and comments to a CSV you can sort.

ScrapingBot 8 min read
Endpoint
/music/posts
Cost
1 credit per page
Language
Python 3.9+
Needs
requests
On this page

Labels, artist teams and music marketers all ask the same question about a song on TikTok: who used it, and which of those videos took off? This guide answers it with about 60 lines of Python and one endpoint.

You will fetch one page of videos for a sound, then follow the cursor through the whole list without missing or double-counting a video, and finally save a CSV sorted by views. The responses shown are real, trimmed with … where they run long.

Before you start

  • A ScrapingBot API key. Create a free account for 100 credits, enough for 100 pages of videos. No card needed.
  • Python 3.9 or newer and pip install requests.
  • A sound ID. This guide uses 7682907287056337667, the original sound behind a lo-fi house clip by @aetherrr.flp.

Find the sound ID

Open the sound on TikTok. Its page URL has the form tiktok.com/music/<sound-name>-<id>, and the number at the end is the sound ID. You can also send the whole sound URL as url instead of music_id.

If all you have is a video, fetch it with the / endpoint and read data.music_info. For a NASA video the sound block looked like this:

"music_info": {
  "id": "7692161536332253965",
  "title": "original sound - NASA",
  "author": "NASA",
  "duration": 136,
  "original": false
}

Fetch one page of videos

Every TikTok call is a POST to https://scrapingbot.io/api/v1/tiktok with the endpoint name and its parameters in the body:

curl -X POST https://scrapingbot.io/api/v1/tiktok \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endpoint": "/music/posts", "params": {"music_id": "7682907287056337667", "count": 30}}'

The response holds a page of videos, a cursor for the next page and a hasMore flag:

{
  "success": true,
  "data": {
    "videos": [
      {
        "video_id": "7682907275505224982",
        "title": "luna eterna #lofihouse #electronicmusic #club #fyp #2000smusic",
        "play_count": 353558,
        "digg_count": 53947,
        "comment_count": 741,
        "create_time": 1788816252,
        "play": "https://v19.tiktokcdn.com/…",
        "author": { "id": "7002179649582711813", "unique_id": "aetherrr.flp", "nickname": "aether" }
      },
      …
    ],
    "cursor": 12,
    "hasMore": 1
  },
  "processed_time": 1.9064,
  "duration": "1.91",
  "statusCode": 200,
  "creditsUsed": 1
}

Three fields matter for paging. videos is this page. cursor goes back unchanged in the next request. hasMore is 1 while there are more pages and 0 at the end. digg_count is TikTok's name for likes, and create_time is a Unix timestamp.

The full script

Save this as sound_videos.py. It wraps the API call with retries, walks every page, keeps the first copy of each video and writes a CSV.

import csv
import time
from datetime import datetime, timezone

import requests

API = "https://scrapingbot.io/api/v1/tiktok"
HEADERS = {"x-api-key": "YOUR_API_KEY"}


def call(endpoint, params, tries=3):
    """One API call. Failed calls are refunded, so a retry costs nothing extra."""
    for attempt in range(tries):
        res = requests.post(API, headers=HEADERS, timeout=60,
                            json={"endpoint": endpoint, "params": params})
        body = res.json()
        if body.get("success"):
            return body["data"]
        if res.status_code in (400, 401, 402):  # bad input, bad key, no credits
            raise RuntimeError(body.get("error"))
        time.sleep(2 ** attempt)  # 408, 429 or 5xx: wait and try again
    raise RuntimeError(f"{endpoint} kept failing: {body.get('error')}")


def videos_for_sound(music_id, max_pages=100):
    videos, cursor = {}, None
    for page in range(1, max_pages + 1):
        params = {"music_id": music_id, "count": 30}
        if cursor is not None:
            params["cursor"] = cursor
        data = call("/music/posts", params)
        for v in data.get("videos", []):
            videos.setdefault(v["video_id"], v)  # the same video can appear twice
        print(f"page {page}: {len(data.get('videos', []))} videos, "
              f"{len(videos)} unique, hasMore={data.get('hasMore')}")
        if not data.get("hasMore"):  # only 0 / false ends the list
            break
        cursor = data["cursor"]
    return list(videos.values())


FIELDS = ["video_id", "url", "creator", "posted", "views", "likes", "comments", "caption"]


def row(v):
    handle = v["author"]["unique_id"]
    return {
        "video_id": v["video_id"],
        "url": f"https://www.tiktok.com/@{handle}/video/{v['video_id']}",
        "creator": handle,
        "posted": datetime.fromtimestamp(v["create_time"], timezone.utc).date().isoformat(),
        "views": v.get("play_count", 0),
        "likes": v.get("digg_count", 0),
        "comments": v.get("comment_count", 0),
        "caption": v.get("title", ""),
    }


if __name__ == "__main__":
    import sys
    sound_id = sys.argv[1] if len(sys.argv) > 1 else "7682907287056337667"
    max_pages = int(sys.argv[2]) if len(sys.argv) > 2 else 100
    rows = sorted(map(row, videos_for_sound(sound_id, max_pages)),
                  key=lambda r: r["views"], reverse=True)
    path = f"sound_{sound_id}.csv"
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(rows)
    print(f"saved {len(rows)} videos to {path}")

A few choices in that code are deliberate:

  • It stops on hasMore, never on page size. TikTok often returns about 10 videos even when you ask for 30, and a short page in the middle of the list is normal.
  • It keys videos on video_id. Pages can overlap, so a dictionary keeps one copy of each. Use video_id rather than aweme_id, which can be empty.
  • It retries only what can succeed later. A 400 (bad input), 401 (bad key) or 402 (out of credits) will fail the same way again, so it raises. Timeouts, 429 and server errors get up to three tries with a growing pause. Failed calls are refunded, so retries cost nothing.
  • max_pages caps the spend. Each page is 1 credit, so the default of 100 can never cost more than 100 credits per sound.

Run it

export SOUND=7682907287056337667
python sound_videos.py $SOUND

The script prints one line per page and then the file name. The first row of the CSV, from the response above:

video_id,url,creator,posted,views,likes,comments,caption
7682907275505224982,https://www.tiktok.com/@aetherrr.flp/video/7682907275505224982,aetherrr.flp,2026-09-07,353558,53947,741,luna eterna #lofihouse #electronicmusic #club #fyp #2000smusic
…

Open it in a spreadsheet and the creators whose videos did best are at the top. That list is often the most useful output for a music marketer: they already chose the song once, and their audience responded.

Add the sound's own details

/music/info returns the title, author, duration, an audio URL and how many videos use the sound. It is one more credit and tells you how big the export will be before you start. For another sound, 7683546066251827217:

{
  "success": true,
  "data": {
    "id": "7683546066251827217",
    "title": "Hoe Craft",
    "author": "MC",
    "album": "Hoe Craft",
    "duration": 60,
    "original": false,
    "video_count": 218,
    "play": "https://sf16.tiktokcdn-us.com/…",
    "cover": "https://p16-common.tiktokcdn-us.com/…"
  },
  "processed_time": 0.1327,
  "duration": "0.13",
  "statusCode": 200,
  "creditsUsed": 1
}

With 218 videos at roughly 10 per page, exporting that sound takes about 22 calls: 22 credits, or about four-tenths of a cent on the Starter plan ($49.99 for 275,000 credits).

info = call("/music/info", {"music_id": "7683546066251827217"})
print(info["title"], "by", info["author"], "-", info["video_count"], "videos")
# Hoe Craft by MC - 218 videos

Fields you get for each video

FieldWhat it is
video_idThe video's ID. Build the link as tiktok.com/@<unique_id>/video/<video_id>.
titleThe caption, including hashtags.
play_countViews.
digg_countLikes.
comment_countComments.
create_timeWhen it was posted, as a Unix timestamp in seconds.
author.unique_idThe creator's handle; author.id is their numeric ID, which you need for /user/followers.
playA playable video URL. It is signed and expires, so download soon if you need the file.

Where to go from here

Common questions

How do I get all the videos that use a TikTok sound?

Call the /music/posts endpoint with the sound ID, then send the cursor from each response back with the next request until hasMore is 0. De-duplicate on video_id, because the same video can show up on two pages.

Where do I find a TikTok sound ID?

It is the number at the end of the sound page URL (tiktok.com/music/<name>-<id>). If you only have a video, fetch it with the / endpoint and read data.music_info.id.

How many videos come back per page?

Around 10, whatever count says. Page sizes vary from page to page, so never treat a short page as the end of the list; only hasMore: 0 means you are done.

What does it cost to export a sound?

Each page is one call and costs 1 credit, so a sound with 218 videos takes about 22 calls. On the Starter plan that is well under a cent. Failed calls are refunded.

Can I get the audio file of the sound?

Yes. /music/info returns a play URL for the audio along with the title, author, duration and the number of videos that use the sound. Media URLs are signed and expire, so download what you need soon after the call.

Start scraping in the next five minutes.

100 free credits, no credit card. One API key works for websites, TikTok, Instagram, Google, Amazon and ChatGPT.