Home > Blog > How to Get a TikTok User's Liked Videos via API
User Data

How to Get a TikTok User's Liked Videos via API

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

A TikTok profile has a heart tab that lists the videos the account has liked. When it is open, it is one of the most direct signals the platform exposes about a person: not what they published, but what they chose to reward. This guide covers how to read that tab through the PrimeApi user-liked-posts endpoint, how to tell a hidden Liked tab apart from an account that has simply never tapped a heart, and what the resulting list is genuinely useful for once you have it.

The workflow is short: resolve the handle to a secUid, check the profile flags, then page user-liked-posts with count and cursor. The part that surprises people is the failure mode - most accounts you try will answer with an empty list, and that is the platform working as designed rather than the API breaking.

Why most Liked tabs come back empty

TikTok exposes a per-account privacy setting for the liked list. Anyone can switch it to private, and in practice the large majority of accounts do - including most large creators, who tend to close it deliberately. The API reads exactly what an anonymous visitor sees in a browser, so when that setting is closed there is nothing to return.

Critically, a closed Liked tab does not produce an error. You get a well-formed response: statusCode 0, an itemList that is an empty array, and hasMore false. If your code treats "no items" as a transport failure it will retry forever and burn credits on an account that will never answer differently. Plan for the empty case before you write the happy path.

Step 1: resolve the handle and read the flags

user-liked-posts does not accept a username. Its one required parameter is secUid, the long opaque string that identifies an account across TikTok's feed endpoints. You get it from userinfo-by-username, which takes the public handle without the @. If that identifier is new to you, the explainer on what a TikTok secUid is covers where it comes from and why handles are not a substitute.

That same profile call is also your pre-flight check. These are the fields worth reading before you spend a credit on the liked feed:

FieldTypeWhy it matters here
user.secUidstringThe identifier user-liked-posts requires. Store it.
user.openFavoriteboolWhether the account exposes its liked/favourite list. Your cheapest signal.
user.privateAccountboolWhole profile is private - the liked feed will be empty regardless.
user.secretboolAccount is hidden. Same practical result.
stats.diggCountintHow many videos the account has liked in total. The number the tab would show if it were open.
user.idstringPermanent numeric account ID, useful as your primary key.
user.uniqueId, user.nicknamestringHandle and display name for labelling rows.
stats.followerCount, stats.videoCount, stats.heartCountintContext for weighting the account in any downstream model.

Pairing openFavorite with stats.diggCount is what makes empty results interpretable. An account with a diggCount of 40,000 and an empty itemList is hiding the tab. An account with a diggCount of 0 has nothing to hide. Those are two different data points and you should not store them under the same label.

Step 2: call user-liked-posts

The request is https://api.primeapi.co/user-liked-posts with your key in the X-PrimeAPI-Key header.

ParameterRequiredNotes
secUidYesFrom userinfo-by-username.
countNoPage size. 30 is a sensible default.
cursorNo0 for the first page, then the cursor value from the previous response.

curl

# 1. handle to secUid, plus the openFavorite flag
curl -s "https://api.primeapi.co/userinfo-by-username?username=EXAMPLE_HANDLE" \
  -H "X-PrimeAPI-Key: YOUR_API_KEY"

# 2. first page of the liked feed
curl -s "https://api.primeapi.co/user-liked-posts?secUid=MS4wLjABAAAA_YOUR_SECUID&count=30&cursor=0" \
  -H "X-PrimeAPI-Key: YOUR_API_KEY"

Node.js (Axios)

const axios = require("axios");

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

async function likedFeed(username) {
  const profile = await axios.get(BASE + "/userinfo-by-username", {
    params: { username },
    headers: HEADERS
  });

  const user = profile.data.user;
  const stats = profile.data.stats;

  if (user.privateAccount || user.secret) {
    return { state: "private_account", likesGiven: stats.diggCount, items: [] };
  }

  const liked = await axios.get(BASE + "/user-liked-posts", {
    params: { secUid: user.secUid, count: 30, cursor: 0 },
    headers: HEADERS
  });

  const items = liked.data.itemList || [];

  if (items.length === 0) {
    // openFavorite false plus a non-zero diggCount means the tab is closed,
    // not that the account has never liked anything.
    const state = user.openFavorite ? "open_but_empty" : "liked_tab_hidden";
    return { state, likesGiven: stats.diggCount, items: [] };
  }

  return {
    state: "open",
    likesGiven: stats.diggCount,
    cursor: liked.data.cursor,
    hasMore: liked.data.hasMore,
    items
  };
}

What the response contains

The envelope is the same one the other feed endpoints use, which is convenient: a liked feed can run through the same parser as an upload feed.

FieldTypeMeaning
itemListarrayThe liked videos for this page. Empty when the tab is closed.
cursorstringPass into the next request. It is a millisecond timestamp, not a page number.
hasMoreboolFalse when you have reached the end of the readable list.
statusCode, status_code, status_msgint/stringUpstream status. Both numeric keys are present.
extra.logid, log_pb.impr_idstringRequest identifiers. Quote them if you contact support about a specific call.

Each entry in itemList is a full video object. The keys you will actually use:

FieldMeaning
idVideo ID. Your join key to every other Post endpoint.
descCaption text, hashtags included inline.
createTimeUpload time as a Unix timestamp in seconds - the video's age, not the like's.
authorBlock for the creator of the liked video: id, uniqueId, secUid, nickname, signature, verified, avatarLarger, privateAccount.
authorStatsfollowerCount, followingCount, heartCount, diggCount, videoCount, friendCount for that creator.
musicSound behind the clip: id, title, authorName, duration, playUrl, original, isCopyrighted.
challengesHashtags attached to the video, as objects rather than raw strings.
CategoryType, diversificationIdTikTok's own content classification integers. Coarse, but free topic hints.
isAdMarks promoted content. Worth filtering out of any taste model.
anchors, effectStickers, contentsOptional attachments, camera effects and structured caption segments.
item_control.can_repostWhether the video allows reposting.

Two honest caveats. First, digged and collected are viewer-relative flags evaluated against an anonymous session, so they are not meaningful in a server-side pipeline. Second, the item object carries a dozen more keys than the ones above; read one raw response into a file before you write your schema. Per-video engagement totals are best pulled with a follow-up call to post-detail on the video id.

Paging the feed

Paging is identical to the uploads endpoint: send cursor=0, read cursor back out, repeat while hasMore is true. If you already page a creator's uploads, the loop from getting all videos from a TikTok user drops straight in with one URL changed.

Python (Requests)

import requests

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

def liked_posts(sec_uid, count=30, max_pages=20):
    cursor, out = "0", []
    for page in range(max_pages):
        resp = requests.get(
            BASE + "/user-liked-posts",
            params={"secUid": sec_uid, "count": count, "cursor": cursor},
            headers=HEADERS,
            timeout=30,
        )
        body = resp.json()
        items = body.get("itemList") or []
        out.extend(items)
        print("page", page, "items", len(items),
              "balance", resp.headers.get("X-PrimeAPI-Balance"))

        # closed tab on page 0, or the end of the list later on
        if not items or not body.get("hasMore"):
            break
        cursor = body.get("cursor")
    return out

Break on an empty page as well as on hasMore. A closed Liked tab hits the first condition immediately and costs you exactly one credit instead of twenty.

What the Liked tab is good for

Seeding a taste graph

An upload feed tells you what a creator produces; a liked feed tells you what they consume. Collect the author.uniqueId values across someone's likes and you have a ranked list of the accounts they pay attention to, weighted by how often each one appears. Do that across a set of accounts in the same niche and the overlapping authors are the genuine centre of gravity for that community - often very different from whoever has the largest follower count.

Recommendation cold starts

If you are recommending creators or videos to a new user and have nothing to go on, their public likes are a ready-made preference vector. The challenges array plus CategoryType gives you topic labels, music.id gives you sound affinity, and authorStats.followerCount lets you separate the mainstream taste from the niche taste. Build the profile from the first two or three pages; the tail rarely changes the ranking.

Sound and trend discovery

Likes surface tracks earlier than upload feeds do, because people hear a sound before they film to it. Pull music.id from a liked feed, then expand the promising ones - the guide on getting TikTok sound and music info shows how to turn a music ID into the full track record and the videos built on it.

Influence mapping

Likes are a quiet endorsement; reposts are a loud one. Reading both tabs for the same secUid separates passive interest from active amplification, and the endpoints share a parameter shape, so it is a cheap addition to an existing job. The post on reading TikTok reposts covers that side.

Credits, limits and expectations

Every call costs 1 credit, including one that returns an empty list - the request reached the data layer, so it is billed. Only requests rejected up front are free: a missing key ("Please sign up to primeapi.co"), a bad key ("PrimeAPI-Key is not available"), an exceeded rate limit, or an exhausted balance. Watch X-PrimeAPI-Balance on every response rather than guessing. The default rate limit is 100 requests per minute, responses are fetched live with no caching at roughly a second each, and new accounts get 50 free credits, which is enough to test the empty-tab detection against a dozen real handles. Larger sweeps are covered by the tiers on the pricing page, from 2,500 credits on Basic at $9.90 up to 500,000 on Enterprise. Full parameter and error references live in the API documentation, and you can fire a real request against a handle without writing any code in the API playground.

One last expectation to set with whoever is asking for this data: coverage will look thin, and that is not a defect in the pipeline. PrimeApi is an independent service, not affiliated with or endorsed by TikTok or ByteDance, and it returns only what is publicly visible. If an account has closed its Liked tab, no API can open it. Report the hidden count alongside the collected count and the numbers will make sense to everyone reading them.

Frequently asked questions

Why does user-liked-posts return an empty itemList?

Because the account has hidden its Liked tab. TikTok lets every user decide who can see the videos they liked, and most accounts keep that list closed. When it is closed the endpoint still answers with a normal 200 envelope containing itemList as an empty array and hasMore set to false. That is expected behaviour, not an error, and the request still costs 1 credit.

How can I tell a hidden Liked tab from an account that simply has not liked anything?

Call userinfo-by-username first and read two fields together. user.openFavorite tells you whether the account exposes its liked list, and stats.diggCount tells you how many likes it has given. An account with a diggCount in the thousands and an empty itemList is hiding the tab; an account with a diggCount of 0 genuinely has nothing to show.

Can I pass a username to user-liked-posts instead of a secUid?

No. The only required parameter is secUid, the long opaque identifier that starts with MS4wLjABAAAA. Resolve the handle once with userinfo-by-username, store the secUid it returns, and reuse it - handles can be renamed but the secUid stays the same for the account.

How many credits does reading a liked feed cost?

One credit per request, and one request per page. A 30-item page costs 1 credit whether it comes back full or empty, so a creator with 300 public likes costs about 10 credits to collect at count=30. New accounts start with 50 free credits and every response carries the remaining balance in the X-PrimeAPI-Balance header.

Can the API read likes on a private account?

No. PrimeApi returns what an ordinary logged-out visitor can see, so a private account, a hidden Liked tab or a region-restricted profile all come back without items. There is no parameter that unlocks them, and no endpoint on the service bypasses a privacy setting.

How do I page through a long liked list?

Send cursor=0 on the first call, then take the cursor value out of each response and pass it into the next request while hasMore is true. Stop when hasMore is false or when itemList comes back empty, and keep count around 30 so a single failed page is cheap to retry.

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