DEV Community

Ethan Walker
Ethan Walker

Posted on

From TikTok Username to Post Metrics: A Python API Workflow

Cover Image
A TikTok username is easy for a person to recognize, but it is not the identifier a post-collection request needs. A reliable workflow has two stages: resolve the public username into sec_uid, then use that value to request a bounded sample of public posts.

This tutorial builds that chain with Python and the Scrapeless Scraping API. It also shows how to keep identifiers, timestamps, missing fields, and sample scope intact so the resulting metrics can be audited later.

What the two requests accomplish

The workflow uses one HTTP endpoint and two actors:

  1. scraper.tiktok.user.detail accepts unique_id, the visible username without the leading @.
  2. The profile response can contain account_id, unique_id, and sec_uid alongside public profile fields.
  3. scraper.tiktok.user.work accepts the returned sec_uid, plus cursor and count.
  4. Its response contains an items array of observed public posts.

The TikTok User Detail documentation and User Work documentation describe those two actor surfaces.

Treat the returned list as a sample. Although the request documents a cursor input, a single response does not prove that you collected a creator's complete history. Record what you requested and what the API returned.

Prerequisites

You need:

  • Python 3 with the standard library
  • A Scrapeless API token in SCRAPELESS_API_KEY
  • A public username in TIKTOK_USERNAME
  • A permitted purpose and a retention period for the collected data

Do not paste credentials into the script or commit them to a repository.

Build one reusable actor client

Both requests use the same endpoint and authentication header, so start with a small helper. It accepts the actor name and actor-specific input, then returns decoded JSON.

import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen

ENDPOINT = "https://api.scrapeless.com/api/v1/scraper/request"
TOKEN = os.environ["SCRAPELESS_API_KEY"]


def run_actor(actor, actor_input):
    payload = json.dumps({"actor": actor, "input": actor_input}).encode()
    request = Request(
        ENDPOINT,
        data=payload,
        headers={
            "x-api-token": TOKEN,
            "content-type": "application/json",
        },
        method="POST",
    )
    try:
        with urlopen(request, timeout=60) as response:
            return json.load(response)
    except HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"actor request failed: {exc.code} {body}") from exc
Enter fullscreen mode Exit fullscreen mode

This is ordinary HTTP and JSON handling. The application does not need a DOM selector or browser parser because the actor returns structured data.

Step 1: resolve the username

Read the username from the environment and remove a leading @. Send the normalized value as unique_id.

USERNAME = os.environ["TIKTOK_USERNAME"].lstrip("@")

profile = run_actor(
    "scraper.tiktok.user.detail",
    {"unique_id": USERNAME},
)

sec_uid = profile.get("sec_uid")
if not sec_uid:
    raise RuntimeError("profile response did not contain sec_uid")
Enter fullscreen mode Exit fullscreen mode

Keep account_id, unique_id, and sec_uid as strings. Large numeric-looking identifiers can lose digits when spreadsheets or JavaScript-based tools coerce them into numbers. The handle is useful to readers, while sec_uid is the bridge to the post actor.

An absent sec_uid is a collection state, not a reason to invent an identifier from a URL. Keep the profile response and record that post retrieval could not continue.

Step 2: request a bounded post sample

Now call the post actor with the resolved identifier. This example asks for 20 items from the initial documented cursor.

posts_response = run_actor(
    "scraper.tiktok.user.work",
    {"sec_uid": sec_uid, "cursor": "0", "count": 20},
)

items = posts_response.get("items") or []
Enter fullscreen mode Exit fullscreen mode

Save the raw response before transforming it. Raw JSON lets you revise field mappings later without collecting the same public page again.

from datetime import datetime, timezone

collected_at = datetime.now(timezone.utc).isoformat()

with open("tiktok-posts-raw.json", "w", encoding="utf-8") as output:
    json.dump(posts_response, output, ensure_ascii=False, indent=2)
Enter fullscreen mode Exit fullscreen mode

Select fields for an analysis table

A post item can expose identity, description, time, public engagement counters, format details, hashtags, music, subtitles, and flags. Start with the smallest field set that answers your question.

def normalize_post(item):
    return {
        "collected_at": collected_at,
        "account_unique_id": str(profile.get("unique_id") or USERNAME),
        "account_id": str(profile.get("account_id") or ""),
        "post_id": str(item.get("id") or item.get("post_id") or ""),
        "post_url": item.get("url") or item.get("post_url") or "",
        "description": item.get("description") or "",
        "created_at": item.get("create_time") or item.get("date"),
        "play_count": item.get("play_count"),
        "like_count": item.get("like_count"),
        "comment_count": item.get("comment_count"),
        "share_count": item.get("share_count"),
        "collect_count": item.get("collect_count"),
        "repost_count": item.get("repost_count"),
        "is_pinned": item.get("is_pinned"),
    }


rows = [normalize_post(item) for item in items]
print(json.dumps(rows, ensure_ascii=False, indent=2))
Enter fullscreen mode Exit fullscreen mode

Notice that missing metrics remain None in Python and null in JSON. Replacing them with zero would claim that the platform reported a definite zero. Blank text is less ambiguous, so descriptions can safely fall back to an empty string when that suits the export schema.

Preserve the boundaries of the sample

Store these collection facts beside every batch:

  • requested username
  • returned unique_id and sec_uid
  • request cursor
  • requested count
  • returned item count
  • UTC collection time
  • request success or failure state

The distinction matters when a post is missing. It may sit outside the bounded response, be unavailable at collection time, or reflect a failed request. None of those conditions means the post had zero engagement.

The same rule applies to content format. A blank video field does not automatically mean the item is invalid; photo posts and optional media fields need tolerant parsing. Use the structure actually returned and retain an unknown state when it is insufficient.

Metrics are observations, not attribution

Public play, like, comment, share, collect, and repost counts describe the post at collection time. They do not identify unique viewers, purchasers, or the cause of a conversion. A single snapshot gives a level. Two timestamped snapshots can support a delta when both observations refer to the same post and metric.

If you operationalize this workflow, the Scraping API product page explains where the TikTok actors fit within Scrapeless.

Common implementation mistakes

  • Passing unique_id directly to the post actor instead of resolving sec_uid
  • Converting account or post IDs to numbers
  • Treating the first items response as a complete archive
  • Filling missing counters with zero
  • Dropping the post URL and raw response needed for review
  • Logging the API token in an exception or debug payload
  • Reporting play count as unique reach or campaign revenue

Conclusion

The smallest reliable TikTok post-metrics pipeline is a two-request chain. Resolve a known public username, use the returned sec_uid to request a bounded post sample, and save both raw and normalized records with a collection timestamp. The value comes from preserving identity and scope as carefully as the metrics themselves.

FAQ

Why does the post actor need sec_uid?

The documented User Work actor identifies the account with sec_uid. The profile actor provides that value when given the public username.

Can one response contain every post?

Do not assume it does. Keep the cursor, requested count, returned count, and collection time. Continue only when the current API response and documentation provide a verified rule.

Should missing engagement values become zero?

No. Keep them null or explicitly unknown unless the source returned a definite zero.

Is public TikTok data collection legal?

Legality depends on the jurisdiction, purpose, access method, fields, and applicable terms. Collect only necessary public data, define retention, restrict access, and obtain advice for the intended use.

Disclaimer: This article is for technical education and does not provide legal advice. Follow applicable laws, platform terms, and organizational policies when collecting or using public web data.

Top comments (0)