Skip to content

Fetching User Posts

The EnsembleData TikTok API enables you to fetch a user's posts and, if publicly available, the posts they have liked. Let's first look at an example profile to get our bearings.

Here we've got the user profile of Zach King, a popular TikTok creator.

Image

Where to find user posts

The user posts we will get are those that appear in the Videos tab of the user's profile. By default, these post are sorted from newest to oldest, and the API, which we will look at below, retrieves the posts in this same order.

Warning

Some users have pinned posts, indicated by the red pinned label on the post in the Videos tab. These posts will appear first in the responses from the EnsembleData API, just as they do on TikTok. Be mindful that these pinned posts are in most cases not the most recent posts by the user. As such you may need to filter these posts out depending on your use case.

Where to find a user's liked posts

Additionally, we can see that Zach King has not made his liked posts public, as indicated by the lock 🔒 on the Liked tab to the right of the Videos tab. Therefore, we won't be able to fetch his liked posts.

User Posts From Username API

API Documentation

Sample Response

For the User Posts From Username endpoint we'll need a username, obviously, and a depth parameter, less obviously. What is the depth parameter, you ask?

The depth parameter tells the API how many chunks of posts to fetch. Each chunk contains 10 posts for this endpoint. Therefore, sending a depth of 1 will fetch 10 posts, a depth of 2 will fetch 20 posts, and so on. You must provide a depth parameter, to tell us how many posts to get.

In addition, the posts

Tip

For more information of using depth throughout the EnsembleData API, see the "What is depth?" page.

We're ready to fetch some user posts. ¡Vamos!

import requests

result = requests.get(
    "https://ensembledata.com/apis/tt/user/posts",
    params={
        "username": "zachking",
        "depth": 1,
        "token": "API_TOKEN",
    }
).json()

posts = result["data"]
next_cursor = result["nextCursor"] # We'll need this later

print("Posts:", len(posts))
from ensembledata.api import EDClient

client = EDClient("API_TOKEN")
result = client.tiktok_user_posts_from_username(
    username="zachking",
    depth=1,
)

posts = result.data["data"]
next_cursor = result.data["nextCursor"] # We'll need this later

print("Posts:", len(posts))
import { EDClient } from "ensembledata";

const client = new EDClient({ token: "API_TOKEN" });
let result = await client.tiktok.userPostsFromUsername({
    username: "zachking",
    depth: 1,
});

let posts = result.data.data;
let nextCursor = result.data.nextCursor; // We'll need this later

console.log("Posts:", posts.length);

Output:

Posts: 10

Great, we've got the most recent 10 posts! Well... not quite; the user may have pinned some posts at the top of their profile. Note: it's possible, though unlikely, the pinned posts are the most recent. To be sure, you should check their timestamps.

Filtering out pinned posts

For simplicity, let's see how we can filter the pinned posts out.

posts = [post for post in posts if not post["is_top"]]
print("Posts:", len(posts))
posts = posts.filter(post => !post.is_top);
console.log("Posts:", posts.length);

Output:

Posts: 8

Piece of cake!

Now let's talk about the options you have for fetching more posts.

Using depth

What is depth?

The simplest way to fetch more posts is to simply send a larger depth parameter. For example, to fetch 50 posts, instead of 10, you would send a depth of 5 instead of 1. However, if you've already sent a request to get the first 10 posts, sending a depth of 5 will get you 50 posts, including the 10 you already have... which is not ideal. In this case, you may want to use the cursor parameter instead.

Using the cursor

What is a cursor?

Each response contains a nextCursor value. You can use this cursor to get the next chunk of posts. Here's how you can do it:

result = requests.get(
    "https://ensembledata.com/apis/tt/user/posts",
    params={
        "username": "zachking",
        "depth": 1,
        "cursor": next_cursor,
        "token": "API_TOKEN",
    }
).json()

next_posts = result["data"]
next_cursor = result["nextCursor"]
result = client.tiktok_user_posts_from_username(
    username="zachking",
    depth=1,
    cursor=next_cursor, 
)

next_posts = result.data["data"]
next_cursor = result.data.get("nextCursor")
result = await client.tiktok.userPostsFromUsername({
    username: "zachking",
    depth: 1,
    cursor: nextCursor,
});

let nextPosts = result.data.data;
nextCursor = result.data.nextCursor;

Combining depth and cursor

You can also combine the depth and cursor parameters to get a specific number of posts, starting from where you left off. For example, let's get the first 10 posts, then get the next 50 posts if there is a nextCursor. If there is no next cursor, it means there are no more posts.

posts = list()
result = requests.get(
    "https://ensembledata.com/apis/tt/user/posts",
    params={
        "username": "zachking",
        "depth": 1,
        "token": "API_TOKEN",
    }
).json()

posts.extend(result["data"])
next_cursor = result.get("nextCursor", None)

if next_cursor is not None:
    result = requests.get(
        "https://ensembledata.com/apis/tt/user/posts",
        params={
            "username": "zachking",
            "depth": 5,
            "cursor": next_cursor,
            "token": "API_TOKEN",
        }
    ).json()

    posts.extend(result["data"])

print("Posts:", len(posts))
posts = list()
result = client.tiktok_user_posts_from_username(
    username="zachking",
    depth=1,
)
posts.extend(result.data["data"])
next_cursor = result.get("nextCursor")

if next_cursor is not None:
    result = client.tiktok_user_posts_from_username(
        username="zachking",
        depth=5,
        cursor=next_cursor,
    )
    posts.extend(result.data["data"])

print("Posts:", len(posts))
let posts = [];
let result = await client.tiktok.userPostsFromUsername({
    username: "zachking",
    depth: 1,
});
posts.push(...result.data.data);
let nextCursor = result.data.nextCursor;

if (nextCursor != undefined) {
    result = await client.tiktok.userPostsFromUsername({
        username: "zachking",
        depth: 5,
        cursor: nextCursor,
    });
    posts.push(...result.data.data);
}
console.log("Posts:", posts.length);

Output:

Posts: 60

Specify an oldest timestamp

The User Posts From Username endpoint also accepts an oldest_createtime parameter (unix timestamp). This parameter acts as a stopping condition when used in combination with the depth parameter. Internally, depth represent the number of requests the EnsembleData API will try to send in sequence. If you specify an oldest_createtime, the API will stop fetching posts when it reaches a post with a timestamp older than the one you specified.

For example, you may want to fetch up to 100 of a user's posts, but only those which are more recent than a certain date. You can do this by specifying a depth of 10, and an oldest_createtime of the cutoff time.

result = requests.get(
    "https://ensembledata.com/apis/tt/user/posts",
    params={
        "username": "zachking",
        "depth": 10,
        "oldest_createtime": 1723806137,
        "token": "API_TOKEN",
    }
).json()
result = client.tiktok_user_posts_from_username(
    username="zachking",
    depth=10,
    oldest_createtime=1723806137,
)
let result = await client.tiktok.userPostsFromUsername({
    username: "zachking",
    depth: 10,
    oldestCreatetime: 1723806137,
});

Warning

The oldest_createtime only tells the API when to stop fetching posts. However, the posts are fetched in chunks. In the last chunk of fetched posts there may be some posts newer, and some older than the oldest_createtime you specified. Therefore, some of the posts returned by the API are likely to be older than the oldest_createtime you specified. You should filter these out in your application if required. We don't filter them out to give you the flexibility to decide what to do with them.

Alternative method

Sample Response

You can optionally tell the API to use a different method to scrape the user's posts by passing alternative_method as True. The reponse payload will is almost identical to the default method, though there may be some extra information. For example, you will find video caption information only using the alternative method under cla_info.

result = requests.get(
    "https://ensembledata.com/apis/tt/user/posts",
    params={
        "username": "zachking",
        "depth": 1,
        "alternative_method": True,
        "token": "API_TOKEN",
    }
).json()
result = client.tiktok_user_posts_from_username(
    username="zachking",
    depth=1,
    alternative_method=True,
)
let result = await client.tiktok.userPostsFromUsername({
    username: "zachking",
    depth: 1,
    alternativeMethod: true,
});

User Posts From Secuid API

API Documentation

Sample Response

This endpoint is functionally the same as the User Posts From Username API, the only difference being you need to supply the secuid of the user instead of the username.

result = requests.get(
    "https://ensembledata.com/apis/tt/user/posts-from-secuid",
    params={
        "secUid": "MS4wLjABAAAALJEOFGMMb1NGn73hAlyCaNThVNS4NE9WfE-T4JA_mtg",
        "depth": 1,
        "token": "API_TOKEN",
    }
).json()
result = client.tiktok_user_posts_from_secuid(
    sec_uid="MS4wLjABAAAA...",
    depth=1,
)
let result = await client.tiktok.userPostsFromSecuid({
    secUid: "MS4wLjABAAAA...",
    depth: 1,
});

The endpoint accepts all the same parameters as the User Posts From Username API. Please refer to the above sections for more details on how you can use this endpoint.

User Liked Posts API

API Documentation

Sample Response

The User Liked Posts endpoint is used to fetch the posts a user has liked. This endpoint requires a user's sec_uid, and uses a cursor to fetch more posts when available.

result = requests.get(
    "https://ensembledata.com/apis/tt/user/liked-posts",
    params={
        "secUid": "MS4wLjABAAAAzdgqSwTVcRLGFPY7J-JnwnH3QQQ8sozmxYSmE5Fxeuql6jjokm5Lui0tl_WrDYWD",
        "token": "API_TOKEN",
    }
).json()["data"]

posts = result["liked_posts"]
next_cursor = result.get("nextCursor", None)

if next_cursor is not None:
    result = requests.get(
        "https://ensembledata.com/apis/tt/user/liked-posts",
        params={
            "secUid": "MS4wLjABAAAAzdgqSwTVcRLGFPY7J-JnwnH3QQQ8sozmxYSmE5Fxeuql6jjokm5Lui0tl_WrDYWD",
            "cursor": next_cursor,
            "token": "API_TOKEN",
        }
    ).json()["data"]
    more_posts = result["liked_posts"]
    next_cursor = result.get("nextCursor", None)
result = client.tiktok_user_liked_posts(
    sec_uid="MS4wLjABAAAA...",
)
posts = result.data["liked_posts"]
next_cursor = result.data.get("nextCursor")

if next_cursor:
    result = client.tiktok_user_liked_posts(
        sec_uid="MS4wLjABAAAA...",
        cursor=next_cursor,
    )
    more_posts = result.data["liked_posts"]
    next_cursor = result.data.get("nextCursor")
let result = await client.tiktok.userLikedPosts({
    secUid: "MS4wLjABAAAA...",
});
let posts = result.data.liked_posts;
let nextCursor = result.data.nextCursor;

if (nextCursor != undefined) {
    result = await client.tiktok.userLikedPosts({
        secUid: "MS4wLjABAAAA...",
        cursor: nextCursor,
    });
    let morePosts = result.data.liked_posts;
    nextCursor = result.data.nextCursor;
}

Note

Many users have their liked posts set to private, and in this case you will not be able to fetch their liked posts.