Get a TikTok Sound ID from a Video and Look Up Its Music Metadata

Published October 20, 2026

Two saved observations make a useful test dataset:

  • Video 7697307919943060767, by khaby.lame: a reported duration of 9,500 milliseconds.
  • Sound 7697308007327730462: title original sound - khaby.lame, owner handle khaby.lame, duration 9 seconds, reported user_count=30.

These were observed together during collection, not promised as permanent live results. The developer task is to preserve the first record's sound reference and resolve the second record—not to send the video ID to a music endpoint.

One video, two identifiers: 7697307919943060767 is not the sound ID

video-data requires video_id; music-data requires music_id. Both accept decimal strings, but those strings identify different objects. A title or matching creator handle cannot replace the association recorded on the video.

The observed video response exposed data.aweme_detail.aweme_id, creator attribution and music information. Its sound-identifier property was not enumerated in the verified contract. Inspect the returned detail before naming that property in an adapter; this guide deliberately does not invent a path such as music.music_id.

If starting from a creator, use username lookup for a known handle. username is the handle, user_id is a numeric-looking account identifier, and sec_user_id is a separate opaque identifier. They are not interchangeable. A creator feed can supply video candidates; preserve confirmed cursors, deduplicate video IDs and stop on repeated checkpoints. Multi-page cursor direction and combinations still need verification.

Keep videoId and musicId as strings before the handoff

Both sample identifiers exceed JavaScript's Number.MAX_SAFE_INTEGER, 9007199254740991. Do not write them as number literals or repair them with String() after ordinary parsing has rounded them.

This server-side JavaScript helper uses raw numeric source text when the runtime supports it, and fails rather than silently accepting an unsafe integer otherwise. A lossless JSON library is another option. Quoted IDs remain strings throughout.

function parsePreservingIds(text) {
  return JSON.parse(text, (_key, value, context) => {
    if (typeof value === "number" && Number.isInteger(value)
        && !Number.isSafeInteger(value)) {
      if (!context?.source) {
        throw new Error("Use a runtime with JSON source access or lossless parsing.");
      }
      return context.source;
    }
    return value;
  });
}

function id(value) {
  if (typeof value !== "string" || !/^\d+$/.test(value)) {
    throw new Error("Expected an exact decimal ID string.");
  }
  return value;
}

async function tiktok(operation, params) {
  const key = process.env.OPENDATA_API_KEY;
  if (!key) throw new Error("Set your authorized platform API key.");
  const url = new URL(
    `https://api.opendata-api.com/api/project/tiktok/${operation}`
  );
  url.searchParams.set("key", key);
  for (const [name, value] of Object.entries(params)) {
    url.searchParams.set(name, value);
  }
  const response = await fetch(url, { signal: AbortSignal.timeout(30000) });
  const raw = await response.text();
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  const gateway = parsePreservingIds(raw);
  if (gateway.code !== 200) throw new Error("Gateway business failure.");
  const payload = gateway.data;
  if (payload?.status !== "ok" || payload?.data?.status_code !== 0) {
    throw new Error("Missing or unsuccessful TikTok payload.");
  }
  return payload.data;
}

Obtain your authorized platform key from the dashboard; static request templates use key=YOUR_API_KEY. Keep real keys server-side. Because authentication travels in the URL, redact query strings from logs and error reports. Opening this article or copying code makes no request; executing a lookup does.

HTTP success and the gateway's code, message and data business result are separate checks. The helper also checks the observed operation-level status fields instead of treating an HTTP 200 response as usable metadata.

Pass the video's music identifier to the music lookup

Run the video request intentionally, then inspect its returned detail locally:

const videoId = id("7697307919943060767");
const video = await tiktok("video-data", { video_id: videoId });
const detail = video.aweme_detail;
if (id(detail?.aweme_id) !== videoId) {
  throw new Error("Video identity mismatch.");
}

Locate the music reference in detail using a local debugger. Confirm the identifier property and preserve its exact string value before automating extraction. Missing music information should stop the chain, not trigger a guess from the caption or title.

For the saved observation, that handoff value was 7697308007327730462. Use the following value only after confirming it against the video record you received; otherwise substitute that record's verified sound ID:

const musicId = id("7697308007327730462");
const music = await tiktok("music-data", { music_id: musicId });
const sound = music.music_info;
if (id(sound?.id_str) !== musicId) {
  throw new Error("Sound identity mismatch.");
}
const observation = {
  videoId,
  musicId,
  fetched_at: new Date().toISOString(),
  title: sound.title,
  sound_duration_seconds: sound.duration,
  reported_sound_user_count: sound.user_count
};

This verifies the returned sound identity, not the video's unknown extraction property. A production adapter must validate that property explicitly. Missing records and changed availability are reasons to retain an incomplete observation, not fabricate a successful mapping.

For adjacent products, APIs for similar apps can be requested through the contact page, and private source-code deployment is available.

Read a 9,500 ms video beside a nine-second original sound

Normalize units before comparing: 9500 / 1000 gives a video duration of 9.5 seconds, while the observed music_info.duration is 9 seconds. Store the original values and units alongside any normalized measurement.

Do not silently round 9.5 to 9 and declare an exact match. The difference alone establishes neither an extraction failure nor a recording fingerprint. It does not prove how the sound was trimmed, whether playback loops, or whether the metadata measures the same interval. Those would require evidence beyond these two records.

The video duration's exact nested field was not established here. Validate it before implementing conversion; never assume every field named duration uses milliseconds.

Keep user_count=30 separate from video views and audience reach

The sound record's user_count=30 is a reported usage-related count. It is not 30 listeners, 30 unique creators, 30 returned video rows, or a measurement of the video's reach. Store its collection time and do not use it as a completeness target for another endpoint.

Comments have a similar counting trap. One observed comment page contained 50 rows beside a reported total of 6,574. Another returned comments=null and no continuation. Neither establishes a complete discussion archive or proves a video does not exist. Reply counts do not guarantee reply bodies, and no separate reply-fetch operation is documented here. The response cursor to request max_cursor handoff remains unverified; do not publish a collection loop on that assumption.

An original sound title is a TikTok sound label, not verified identification of a commercially released song. Owner attribution is not proof of composition ownership, and this lookup does not provide audio fingerprinting.

To investigate videos using that sound, the separate music-feed takes music_id and optional max_cursor. Its response schema, continuation behavior and coverage are unverified. Do not invent a related-video loop or promise all uses of the sound.

Other discovery routes have their own identity boundaries: a hashtag-name lookup can yield ch_info.cid for a challenge-ID feed, while region-selected trending results are feed samples, not a complete country ranking or evidence of viewer residence. Neither fills gaps in sound coverage.

The official video-query documentation, an open-source sound client and a Chinese-facing music reference corroborate the identifier-based task. Their schemas and access rules are not this project's contract.

Finally, play_url is playback metadata, not a reuse license or a permanent download guarantee. Confirm platform terms, privacy obligations and audio rights independently. The defensible output is a timestamped video-to-sound association with explicit unknowns—not a song-rights decision, audience census or complete sound archive.