---
title: "Get TikTok song details"
method: GET
path: "/tiktok/song"
tags: ["tiktok"]
---

# Get TikTok song details

`GET /tiktok/song`

## Request

- Base URL: `https://www.socialcrawl.dev/v1`
- URL: `https://www.socialcrawl.dev/v1/tiktok/song`
- Auth: API key in header `x-api-key`

Returns details about a specific TikTok sound/song including title, artist, duration, and cover image. The number of videos using the sound is published twice: `post.engagement.views` keeps the value this endpoint has always returned, and `post.ext.use_count` is that same number labelled as a video count (`post.ext.use_count_unit` is `videos`). It is not a play count. To record this count every day, create a Monitor that tracks `post.engagement.views`; see /docs/recipes/track-sound-growth.

## Query parameters

- `clipId` string, required

## Headers

- `Cache-Control` string
- `Idempotency-Key` string

## Response `200`

Successful response

- object
  - `success` boolean, required: Whether the request succeeded
  - `platform` string, required: Platform name
  - `endpoint` string, required: API endpoint path
  - `data` object, required: Platform-specific response data
    - `id` string: Platform-specific post ID (always a string; numeric upstream IDs are stringified)
    - `url` string, nullable: Direct URL to the post on the source platform
    - `content` object: Post content fields (text, media, thumbnail, duration)
      - `text` string, nullable: Post caption, description, or text content
      - `media_urls` union: URL(s) of the primary media. Single string for video/photo posts; array of strings for carousels.
        - string
        - string[]
      - `thumbnail_url` string, nullable: URL to thumbnail image
      - `duration_seconds` integer, nullable: Video/clip duration in seconds (null for non-video posts)
    - `author` object: Subset of Author fields for the post creator
      - `username` string, nullable: Author username
      - `display_name` string, nullable: Author display name
      - `avatar_url` string, nullable: URL to author profile picture
      - `verified` boolean, nullable: Whether the author account is verified
    - `engagement` object: Engagement counts
      - `views` integer, nullable: View count (if available). For Instagram video/reels this is the play count: the headline 'Views' the IG app shows. Instagram removed per-post play counts from its public web pages in August 2026, so `GET /v1/instagram/post` now fills the count automatically from a second source within the same call: videos and reels keep a numeric `views` with no extra endpoint or workaround. Note: since mid-July 2026 Instagram's play count is Instagram-only. It no longer includes Facebook crosspost views (Meta-side change; the app UI changed identically). For combined reach, also fetch the Facebook crosspost via `/v1/facebook/post`.
      - `likes` integer, nullable: Like / reaction count
      - `comments` integer, nullable: Comment count. Note: on `GET /v1/instagram/post` this is Instagram's `edge_media_to_parent_comment.count` (top-level comments); the IG list endpoints report the mobile `comment_count` total (includes replies). Both land on this same field.
      - `shares` integer, nullable: Share / repost / retweet count. On Instagram this is `reshare_count` (the paper-plane Share / Send count) and it exists only on Instagram's mobile surface, so it is returned by `/v1/instagram/post/stats`, `/v1/instagram/profile/reels/full`, and `/v1/instagram/profile/posts/full`. The web-sourced Instagram list and search endpoints return `null` (never fabricated).
      - `saves` integer, nullable: Save / bookmark count. `null` on Instagram: the save count is platform-private and Instagram exposes no numeric save metric on any surface (never fabricated).
    - `flags` object: Boolean flags on the post
      - `nsfw` boolean, nullable: NSFW flag (null when platform does not surface)
      - `spoiler` boolean, nullable: Spoiler flag (null when platform does not surface)
      - `pinned` boolean, nullable: Pinned-to-profile flag (null when platform does not surface)
      - `deleted` boolean: Whether the post is tombstoned (always present)
      - `likes_hidden` boolean, nullable: Present and `true` when the creator has hidden the like count. `engagement.likes` is `null` in that case (never the platform's decoy preview number). Absent on normal posts.
      - `comments_hidden` boolean, nullable: Present and `true` when the upstream reports the comment count as hidden. `engagement.comments` is `null` in that case. Absent on normal posts.
      - `shares_hidden` boolean, nullable: Boolean at post.flags.shares_hidden
      - `views_hidden` boolean, nullable: Boolean at post.flags.views_hidden
      - `saves_hidden` boolean, nullable: Boolean at post.flags.saves_hidden
    - `published_at` union: Post creation timestamp as an ISO 8601 UTC string. When the upstream sent a Unix epoch it is converted here and the raw epoch is preserved under `post.ext.published_at_epoch` for one deprecation cycle.
      - string
      - integer
    - `ext` object, nullable: Platform-specific passthrough fields for cross-endpoint chains (present only on platforms that surface them)
      - `music_id` string, nullable: TikTok music/clip id (exact string): pass to `/v1/tiktok/song/videos?clipId=` to find videos using the same sound
      - `author_id` string, nullable: The creator's platform-native numeric user id. On TikTok search and list items, pass to `/v1/tiktok/profile?user_id=` for the creator's current follower count (survives username changes). On Instagram `search/reels` items it is present on every row from every serving source, accepted by `/v1/instagram/basic-profile?userId=`. On Facebook it appears when the upstream exposed a numeric actor id and no real handle.
      - `author_url` string, nullable: The posting account's profile URL, so a matching post can be followed to its account. Present on `/v1/facebook/search/posts` rows: the vanity or `/people/<name>/<id>/` URL when Facebook sends one, otherwise `https://www.facebook.com/profile.php?id=<numeric id>` built from the account id. Pass it as `url` to `/v1/facebook/profile` (or to `/v1/prism/profiles` with `platform: facebook`). Null when the row names neither an account URL nor a numeric account id. Absent on other endpoints.
      - `author_followers` integer, nullable: The creator's follower count as embedded in the search payload itself, when the search source happens to carry one. On Instagram `search/reels` it is null on a plain call, because Instagram stopped sending follower counts in its search payload in August 2026; send `include=creator` (or `country`) and it is filled from the creator's profile, 2 credits per creator looked up. It stays null when that lookup does not resolve.
      - `author_following` integer, nullable: Numeric at post.ext.author_following
      - `author_posts_count` integer, nullable: Numeric at post.ext.author_posts_count
      - `in_reply_to_id` string, nullable: String at post.ext.in_reply_to_id
      - `in_reply_to_username` string, nullable: String at post.ext.in_reply_to_username
      - `author_country` string, nullable: The creator's country as they declare it on Instagram's About this account panel, as a country name (for example `Spain`). It is not where the reel was filmed. Present only on `/v1/instagram/search/reels` when the request sends `include=creator` (or `country`), filled by looking the creator up. Null when Instagram does not publish a country for the creator or the lookup did not resolve, and nothing is guessed; absent on a plain call.
      - `author_public_email` string, nullable: The public contact email on the creator's profile. Present only on `/v1/instagram/search/reels` when the request sends `include=creator`, filled by looking the creator up. Null when the creator lists no public email or the lookup did not resolve; absent on a plain call.
      - `author_public_phone` string, nullable: The public contact phone number on the creator's profile. Present only on `/v1/instagram/search/reels` when the request sends `include=creator`, filled by looking the creator up. Null when the creator lists no public phone or the lookup did not resolve; absent on a plain call.
      - `download_count` integer, nullable: How many times the video has been saved to a device. TikTok only, and distinct from `engagement.saves`: a save keeps the video in a private collection on the platform, a download takes a copy off it, and TikTok counts the two separately. Present on TikTok post, search and post-list responses; absent everywhere else.
      - `region` string, nullable: TikTok only: the country TikTok registers the video to, as an ISO 3166-1 alpha-2 code (normally the creator's account country when they posted). It is not the viewer's country, not the `region` you requested, and not a language. On `/v1/tiktok/trending`, `/v1/tiktok/search`, `/v1/tiktok/search/top`, `/v1/tiktok/profile/videos` and `/v1/tiktok/search/hashtag` it is how you tell which rows are from a given country: filter on it when you need only that country. A row whose source omits the country stays null. The `region` request parameter still only sets the proxy.
      - `subreddit` string, nullable: Reddit subreddit name (search/list items): pass to `/v1/reddit/subreddit/details?subreddit=`
      - `title` string, nullable: String at post.ext.title
      - `selftext` string, nullable: String at post.ext.selftext
      - `upvote_ratio` integer, nullable: Reddit only: the share of a post's votes that are upvotes, as a fraction from 0 to 1 and usually close to 1 (six live rows measured 1, 1, 1, 0.86, 0.75, 1). Fractional despite the integer type this schema emits for every numeric leaf, the same caveat as `author.ext.average_rating`. Populated on `/v1/reddit/search` rows whose source carries it, and null everywhere else on Reddit including `/v1/reddit/post` and `/v1/reddit/subreddit`.
      - `flair` string, nullable: String at post.ext.flair
      - `content_language` string, nullable: The language the platform itself tags the post with, when it sends one: Reddit's own language tag on Reddit lanes, and TikTok's caption-language tag (for example `de`) on `/v1/tiktok/trending?feed=local`. Null when the platform could not tell (Reddit `und`, TikTok `un`, common on short or emoji-only captions); absent where the source sends no tag. Never inferred from the text by us: that is `post.computed.language`.
      - `type` string, nullable: String at post.ext.type
      - `content_type` string, nullable: Leaf at post.ext.content_type
      - `ticker_symbols` string[], nullable: Array at post.ext.ticker_symbols
      - `video_view_count` integer, nullable: Instagram's legacy 3-second-view count. Historical: Instagram stopped serving it in August 2026 (removed from its web post surface along with the web play count), so fresh `/v1/instagram/post` lookups no longer carry it and no source can restore it. Use `engagement.views` (the play count) instead.
      - `ig_play_count` integer, nullable: Instagram-only play count (`ig_play_count`). Since mid-July 2026 Instagram's headline play count (`engagement.views`) no longer includes Facebook crosspost views and equals this value; it is surfaced explicitly so you can tell the Instagram-only figure apart and detect any future re-divergence. For combined Instagram + Facebook reach, also fetch the Facebook crosspost via `/v1/facebook/post`. Present on `/v1/instagram/post/stats` and, since the August 2026 views fix, on `/v1/instagram/post` for video posts.
      - `repost_count` integer, nullable: Numeric at post.ext.repost_count
      - `media_type` string, nullable: Leaf at post.ext.media_type
      - `text_truncated` boolean, nullable: Boolean at post.ext.text_truncated
      - `ip_location` string, nullable: String at post.ext.ip_location
      - `carousel_count` integer, nullable: Numeric at post.ext.carousel_count
      - `remix_count` integer, nullable: Numeric at post.ext.remix_count
      - `audio_cluster_id` string, nullable: Instagram's audio cluster for a reel, as an exact digit string: the id Instagram uses to group different uploads of the same sound. Present on `/v1/instagram/audio/reels` rows and on `/v1/instagram/profile/reels` with `include=stats`; absent on a row when it is not available and on every other surface.
      - `facebook_likes` integer, nullable: Instagram only: the Facebook cross-post share that was EXCLUDED from `engagement.likes` on this row. A post shared to Facebook arrives from some sources with its Facebook likes already folded into the Instagram count, so the share is subtracted and published here, which keeps `engagement.likes` the Instagram-only figure instagram.com shows on the post. Add the two together for the combined number. Absent when the read carried no Facebook share, and absent on a post whose owner hides the like count, because nothing can be subtracted from a null. A response that excluded a share also carries a note in `data._warnings`.
      - `facebook_comments` integer, nullable: Instagram only: the Facebook cross-post share that was EXCLUDED from `engagement.comments` on this row, the comment counterpart of `post.ext.facebook_likes`. Each leaf appears only for the count it actually changed, so a post whose likes are hidden but whose comments were reconciled carries this one alone.
      - `instagram_views` integer, nullable: Instagram only: the Instagram-only play count of a video, the same figure on every Instagram endpoint. `engagement.views` keeps each endpoint's established meaning: on `/v1/instagram/profile/reels`, `/v1/instagram/audio/reels`, `/v1/instagram/search/reels` and `/v1/instagram/search/hashtag` it can include the post's Facebook cross-post views, while on `/v1/instagram/post` and `/v1/instagram/post/stats` it is Instagram-only. Read this leaf when you compare one post across endpoints. Absent on photos and when the source did not report the Instagram-only figure.
      - `facebook_views` integer, nullable: Instagram only: the Facebook cross-post part of a video's play count, the counterpart of `post.ext.instagram_views`. Present only when the source reported the Facebook part, or a combined total above the Instagram figure. Absent does not mean zero: some sources report the Instagram figure alone even for a cross-posted video.
      - `published_at_epoch` integer, nullable: Raw Unix epoch for `published_at` (seconds, or milliseconds when the upstream sent millis). Present only when the upstream sent a numeric epoch that was normalised to the ISO 8601 `published_at` string. Kept for one deprecation cycle for integrations pinned to the numeric form.
      - `usertags` string[], nullable: Array at post.ext.usertags
      - `coauthors` string[], nullable: Instagram collaborative posts (the native "Collab" feature). The full list of co-author accounts on the post, as `{ id, username, full_name, is_verified, profile_pic_url }`. A collab post has ONE producer and appears in every co-author's grid, so `post.author` is whichever account created it, which is not necessarily the profile you queried. The complete set of accounts on a post is `post.author.username` plus every `username` in this array. An empty array means Instagram reports the post as NOT a collab; the field is absent on surfaces that carry no co-author signal, including `/v1/instagram/post` (its web source ships the field permanently empty, so use `/v1/instagram/post/stats` for a single post).
      - `music` string, nullable: Leaf at post.ext.music
      - `sponsor_tags` string[], nullable: Array at post.ext.sponsor_tags
      - `sponsored_disclosed` boolean, nullable: Boolean at post.ext.sponsored_disclosed
      - `location` string, nullable: Leaf at post.ext.location
      - `media` object[], nullable: Array at post.ext.media
        - `type` string: Leaf at post.ext.media.type
        - `url` string, nullable: String at post.ext.media.url
        - `width` integer, nullable: Numeric at post.ext.media.width
        - `height` integer, nullable: Numeric at post.ext.media.height
      - `top_comments` object[], nullable: Array at post.ext.top_comments
        - `id` string, nullable: String at post.ext.top_comments.id
        - `text` string, nullable: String at post.ext.top_comments.text
        - `username` string, nullable: String at post.ext.top_comments.username
        - `likes` integer, nullable: Numeric at post.ext.top_comments.likes
        - `published_at` string, nullable: String at post.ext.top_comments.published_at
      - `hashtags` string[], nullable: Array at post.ext.hashtags
      - `mentions` string[], nullable: Array at post.ext.mentions
      - `quoted_post` string, nullable: Threads quote posts: the post this one quotes, as `{ id, url, author: { id, username }, text, media_urls, thumbnail_url }`. A quote post usually carries no caption or media of its own, and the text and video you see on the post's page belong to this attachment, by a different author. It is returned here rather than merged into `post.content`, so `content.*` always stays the outer post's own content and one account's media is never attributed to another. Absent on posts that are not quote posts.
      - `retweeted_post` string, nullable: Leaf at post.ext.retweeted_post
      - `quote_count` integer, nullable: Numeric at post.ext.quote_count
      - `all_media_urls` string[], nullable: Array at post.ext.all_media_urls
      - `reaction_counts` string[], nullable: Array at post.ext.reaction_counts
      - `share_urn` string, nullable: String at post.ext.share_urn
      - `post_type` string, nullable: String at post.ext.post_type
      - `activity_id` string, nullable: String at post.ext.activity_id
      - `published_at_precision` string, nullable: String at post.ext.published_at_precision
      - `author_urn` string, nullable: String at post.ext.author_urn
      - `author_headline` string, nullable: String at post.ext.author_headline
      - `author_type` string, nullable: String at post.ext.author_type
      - `is_repost_quote` boolean, nullable: Boolean at post.ext.is_repost_quote
      - `article` string, nullable: Leaf at post.ext.article
      - `reaction_type` string, nullable: String at post.ext.reaction_type
      - `download_media_urls` string[], nullable: Array at post.ext.download_media_urls
      - `tags` string[], nullable: Array at post.ext.tags
      - `categoryId` string, nullable: String at post.ext.categoryId
      - `categoryTitle` string, nullable: String at post.ext.categoryTitle
      - `topicCategories` string[], nullable: Array at post.ext.topicCategories
      - `duration` string, nullable: String at post.ext.duration
      - `license` string, nullable: String at post.ext.license
      - `madeForKids` boolean, nullable: Boolean at post.ext.madeForKids
      - `defaultAudioLanguage` string, nullable: String at post.ext.defaultAudioLanguage
      - `hasPaidProductPlacement` boolean, nullable: Boolean at post.ext.hasPaidProductPlacement
      - `caption` string, nullable: String at post.ext.caption
      - `position` integer, nullable: Numeric at post.ext.position
      - `playlistId` string, nullable: String at post.ext.playlistId
      - `videoOwnerChannelId` string, nullable: String at post.ext.videoOwnerChannelId
      - `videoPublishedAt` string, nullable: String at post.ext.videoPublishedAt
      - `description` string, nullable: String at post.ext.description
      - `default_language` string, nullable: String at post.ext.default_language
      - `localizations` string, nullable: Leaf at post.ext.localizations
      - `playlist_item_id` string, nullable: String at post.ext.playlist_item_id
      - `playlist_owner_channel_id` string, nullable: String at post.ext.playlist_owner_channel_id
      - `playlist_owner_title` string, nullable: String at post.ext.playlist_owner_title
      - `channel_id` string, nullable: String at post.ext.channel_id
      - `published_label` string, nullable: String at post.ext.published_label
      - `published_precision` string, nullable: Leaf at post.ext.published_precision
      - `title_maybe_translated` boolean, nullable: Boolean at post.ext.title_maybe_translated
      - `video_count` integer, nullable: Numeric at post.ext.video_count
      - `commerce` string, nullable: Leaf at post.ext.commerce
      - `on_screen_texts` string[], nullable: Array at post.ext.on_screen_texts
      - `topic_tag` string, nullable: String at post.ext.topic_tag
      - `reshare_count` integer, nullable: Numeric at post.ext.reshare_count
      - `dsp_ids` string, nullable: Leaf at post.ext.dsp_ids
      - `topic_tag_id` string, nullable: String at post.ext.topic_tag_id
      - `amazon_shop_lists` string[], nullable: Array at post.ext.amazon_shop_lists
      - `amazon_shop_trending_picks` string[], nullable: Array at post.ext.amazon_shop_trending_picks
      - `amazon_shop_curations` string[], nullable: Array at post.ext.amazon_shop_curations
      - `amazon_shop_socials` string[], nullable: Array at post.ext.amazon_shop_socials
      - `ad` string, nullable: Ad Library envelope (Facebook `adlibrary/ad` and `adlibrary/company/ads` items): page_id, currency, spend, reach_estimate, publisher_platforms, categories, targeted_or_reached_countries, cta_text, cta_type, link_url, is_active, end_date_iso, display_format, title, video_hd_url, video_sd_url. Absent on every non-ad surface.
      - `apple_music` string, nullable: Leaf at post.ext.apple_music
      - `feedback_id` string, nullable: String at post.ext.feedback_id
      - `question_id` string, nullable: Naver KnowledgeiN (`/v1/naver/kin/search`) only: the question's id (Naver's `docId`), read from the row's link. Kin returns one row per answer, so several rows can share it; send `group=question` for one row per question.
      - `answer_no` integer, nullable: Naver KnowledgeiN only: which answer of the question this row is (Naver's `answerNo`), read from the row's link.
      - `answer_nos` integer[], nullable: Naver KnowledgeiN with `group=question` only: every answer number of the question that was on the page, best-ranked first.
      - `published_at_est` string, nullable: Naver KnowledgeiN only: the question's date ESTIMATED from its id (`YYYY-MM-DD`, Korea time), never a published date, so `published_at` stays null. Absent for questions older than the calibration table.
      - `date_basis` string, nullable: How `post.ext.published_at_est` was derived. `kin_docid_estimate`: interpolated from the question id against question pages whose posted date was read.
      - `event` string, nullable: Leaf at post.ext.event
      - `trend` string, nullable: Trend-board envelope: what TikTok's own trend board says about the row. On `/v1/tiktok/hashtags/popular`: `rank`, `country_code`, `period_days`, `posts` and `views` for the window (not lifetime totals), `industry` / `industry_id` / `industry_label` for the first board the hashtag was read from (null for the overall board), `boards` listing EVERY board it ranks on as `{ industry, industry_id, industry_label, rank }` in board order (so a hashtag in the top 3 overall and on an industry board shows both), `popularity_curve` as `{ date, value }` with value 0 to 100 relative to the window's peak, and `top_creators`. On `/v1/tiktok/videos/popular`: `rank`, `country_code`, `period_days`, `order_by`, `period_views`, `organic_views`, `engagement_rate`, `six_second_view_through_rate`, `content_tags`. Absent on every non-board surface.
      - `updated_at` string, nullable: String at post.ext.updated_at
      - `alt_text` string, nullable: String at post.ext.alt_text
      - `board_name` string, nullable: String at post.ext.board_name
    - `_warnings` string[]: Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.
  - `credits_used` integer, required: Number of credits consumed
  - `credits_remaining` integer, nullable, required: Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.
  - `request_id` string, required: Unique request identifier for support
  - `cached` boolean, required: Whether the response was served from cache
  - `idempotent_replay` boolean: True only when this response is an idempotency replay
  - `meta` object: Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.
    - `hint` object: One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.
      - `code` string: creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat
      - `message` string: One sentence for a person or an agent to read.
      - `method` string: GET or POST.
      - `path` string: The call the hint points to.

## Other responses

- `400`: Invalid request - missing or malformed parameters
- `401`: Unauthorized - missing or invalid API key
- `402`: Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit
- `404`: Not found - the endpoint does not exist, or the requested resource was not found upstream
- `405`: Method not allowed - wrong HTTP verb for this endpoint
- `409`: Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation
- `410`: Gone - the discover run's retention passed and its rows were deleted; start a new run
- `413`: Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling
- `422`: Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)
- `429`: Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.
- `500`: Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit
- `502`: Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge
- `503`: Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge
- `504`: Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After

---

[API](https://skmtc.dev/socialcrawl/apis/socialcrawl-api.md) · [All operations](https://skmtc.dev/socialcrawl/apis/socialcrawl-api/llms.txt) · [OpenAPI document](https://skmtc.dev/socialcrawl/apis/socialcrawl-api/revisions/a0ad2511004a?raw)
