Home > Blog > How to Get Replies to a TikTok Comment via API
Post Data

How to Get Replies to a TikTok Comment via API

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

The comments endpoint gives you the top level of a TikTok discussion. It does not give you the arguments, the corrections and the creator's answers that live underneath it, and that is usually where the useful text is. Getting the second level takes a different call and two identifiers instead of one. This guide covers how to harvest comment IDs from post-comments, fan out into post-comment-replies, read the fields that link a reply back to its parent, and assemble both levels into one thread structure with a script you can run as-is.

Why replies need two IDs

The endpoint is post-comment-replies. It takes two required parameters - postId and commentId - plus the optional count and cursor pair used everywhere else in the API. The base URL is https://api.primeapi.co/ and authentication is the single header X-PrimeAPI-Key, which you copy from your profile page after registering. There is no OAuth handshake and no TikTok developer account involved; PrimeApi is an independent service and is not affiliated with or endorsed by TikTok or ByteDance.

Both IDs are mandatory because a comment ID is only meaningful in the context of its video. That has one practical consequence: you cannot start here. Every reply job begins with a pass over post-comments to collect the parent IDs, or with a database you already filled from an earlier pass. The full walkthrough of that first step is in the guide on scraping TikTok comments; what follows here assumes you have the top level and want the rest.

Step one: harvest the parent comment IDs

Page post-comments as normal and keep two fields from every record: cid, which becomes your commentId, and reply_comment_total, which is the number of replies hanging off that comment. Anything with a zero there has nothing to fetch, and calling it anyway costs a credit and returns an empty array.

There is a second shortcut worth taking. Top-level records can carry reply_comment, a short inline preview of the thread that is often a single record. When reply_comment_total is 1 and that preview is populated, you already have the reply and can skip the request entirely. On threads where most comments have exactly one answer, that alone cuts the fan-out substantially.

curl -s "https://api.primeapi.co/post-comments?postId=7191880324966599942&count=50&cursor=0" \
  -H "X-PrimeAPI-Key: YOUR_API_KEY"

If you want to rank the threads before spending anything, sort_extra_score on a top-level comment carries reply_score and show_more_score, the hints TikTok itself uses to decide which sub-threads are worth surfacing. Combined with digg_count and author_pin, that is enough to pick the twenty conversations that matter out of two hundred comments.

Step two: call the replies endpoint

With a postId and a cid in hand the call itself is unremarkable. Set cursor to 0 for the first page and send back the cursor value from the response on each subsequent call.

curl

curl -s "https://api.primeapi.co/post-comment-replies?postId=7191880324966599942&commentId=7192268986179076865&count=10&cursor=0" \
  -H "X-PrimeAPI-Key: YOUR_API_KEY"

Node.js (Axios)

const axios = require("axios");

async function getReplies(postId, commentId, cursor = 0, count = 50) {
  const res = await axios.get("https://api.primeapi.co/post-comment-replies", {
    params: { postId, commentId, count, cursor },
    headers: { "X-PrimeAPI-Key": "YOUR_API_KEY" }
  });
  console.log("Remaining credits:", res.headers["x-primeapi-balance"]);
  return res.data;
}

getReplies("7191880324966599942", "7192268986179076865")
  .then(data => {
    const rows = data.comments || [];
    console.log("replies in thread:", data.total, "returned:", rows.length);
    rows.forEach(r => console.log(r.cid, "parent:", r.reply_id, r.text));
  })
  .catch(err => console.error(err.response ? err.response.data : err.message));

Python (Requests)

import requests

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

def get_replies(post_id, comment_id, cursor=0, count=50):
    resp = requests.get(
        f"{BASE}/post-comment-replies",
        params={"postId": post_id, "commentId": comment_id,
                "count": count, "cursor": cursor},
        headers=HEADERS,
        timeout=20,
    )
    resp.raise_for_status()
    print("Remaining credits:", resp.headers.get("X-PrimeAPI-Balance"))
    return resp.json()

data = get_replies("7191880324966599942", "7192268986179076865")
for r in (data.get("comments") or []):
    print(r["cid"], r["reply_id"], r["digg_count"], r["text"])

The envelope is the same shape as the comments one: comments holds this page of records, total is how many replies the thread contains, cursor is the integer offset for your next call, and has_more is an integer flag - 1 while there is more, 0 at the end. Alongside them sit status_code, status_msg, an extra block and log_pb with the request ID, which is the value to keep in your own logs when a response looks wrong.

The reply record, field by field

A reply looks almost exactly like a top-level comment, which is convenient: one table with a nullable parent column stores both levels. The fields below are the ones that actually appear on a reply record.

FieldWhat it holds
cidThe reply's own ID. Your primary key, and unique across levels.
reply_idThe parent comment ID. On a top-level comment this is "0"; on a reply it carries the cid you passed as commentId, which is what lets you attach the record to its thread.
reply_to_reply_id"0" when the message answers the top-level comment directly. When it holds another reply's ID, the message is answering that reply instead - this is how you rebuild visual nesting inside a flat list.
aweme_idThe post ID, repeated on every record. Useful when you flatten replies from many videos into one table.
textThe reply body, emoji included.
create_timeUnix timestamp in seconds. Sort on this yourself if you need strict chronological order.
digg_countLikes on the reply itself.
is_author_diggedTrue when the video's creator liked the reply. A cheap signal for "the creator saw this".
thread_id / thread_has_morePer-record thread identifiers and a flag indicating more of the thread exists. The envelope has_more is still the value your pager should trust.
comment_languageDetected language code, for example "en". Filter on it before running any text analysis.
reply_commentAlways null here. Replies do not nest their own previews.
status, fold_status, no_showVisibility and moderation flags. Folded or hidden replies still arrive in the payload, so filter on these if you only want what a normal viewer sees.
image_listAttached images, null on a plain text reply.
text_extraMarkup for the mentions and hashtags inside the text. On replies this is where the @handle of the person being answered shows up.
label_listAny labels TikTok has attached, or null.
share_infoBlock with url, title, desc and acl - the deep link back to the reply on tiktok.com.
stick_positionPinning slot, for ordering.
is_comment_translatable, trans_btn_style, translated_text_extraTranslation availability and presentation hints.
collect_stat, seeking_help_model_type, is_high_purchase_intentClassification flags TikTok attaches to the record.

The pair to build on is reply_id and reply_to_reply_id. The first attaches a reply to its parent comment, the second attaches it to a sibling reply when the conversation goes one turn deeper. Because TikTok keeps everything in the parent thread rather than nesting further, you never recurse - one call per parent comment retrieves the entire conversation underneath it, however many turns it ran to.

Rebuilding the whole thread

Here is the full job: page the top level, decide what to expand, page each expanded thread, and emit a nested structure. It skips comments with no replies, uses the inline preview when a comment has exactly one, caps pages at both levels and stops when a cursor fails to advance.

import time
import requests

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

def call(path, params):
    r = requests.get(f"{BASE}/{path}", params=params, headers=HEADERS, timeout=20)
    r.raise_for_status()
    return r.json()

def page_all(path, params, max_pages):
    """Generic count/cursor pager. Returns every comment record it can reach."""
    cursor, pages, out = 0, 0, []
    while pages < max_pages:
        data = call(path, {**params, "cursor": cursor})
        rows = data.get("comments") or []
        if not rows:
            break
        out.extend(rows)
        pages += 1
        if not data.get("has_more"):
            break
        nxt = data.get("cursor", 0)
        if nxt == cursor:          # cursor stalled - stop before looping forever
            break
        cursor = nxt
        time.sleep(0.6)            # stay well inside 100 requests per minute
    return out

def build_thread(post_id, min_replies=2, top_pages=20, reply_pages=10):
    tree = []
    top = page_all("post-comments",
                   {"postId": post_id, "count": 50}, top_pages)
    for c in top:
        total = c.get("reply_comment_total") or 0
        node = {
            "cid": c["cid"],
            "text": c["text"],
            "digg_count": c["digg_count"],
            "create_time": c["create_time"],
            "pinned": bool(c.get("author_pin")),
            "reply_comment_total": total,
            "replies": [],
        }
        if total == 0:
            pass                                   # nothing under it
        elif total == 1 and c.get("reply_comment"):
            node["replies"] = c["reply_comment"]   # free, already inline
        elif total >= min_replies:
            node["replies"] = page_all(
                "post-comment-replies",
                {"postId": post_id, "commentId": c["cid"], "count": 50},
                reply_pages,
            )
        tree.append(node)
    return tree

def print_thread(tree):
    for node in tree:
        flag = "[pinned] " if node["pinned"] else ""
        print(f'{flag}{node["cid"]}  {node["digg_count"]}  {node["text"]}')
        by_cid = {r["cid"]: r for r in node["replies"]}
        for r in node["replies"]:
            parent = r.get("reply_to_reply_id", "0")
            if parent != "0" and parent in by_cid:
                print(f'      @{by_cid[parent]["cid"]} -> {r["text"]}')
            else:
                print(f'    - {r["text"]}')

thread = build_thread("7191880324966599942")
print_thread(thread)
print("comments:", len(thread),
      "replies:", sum(len(n["replies"]) for n in thread))

Two details in that script matter more than they look. The by_cid lookup turns the flat reply list into the two-tier display TikTok shows, using only reply_to_reply_id - no extra requests. And the stalled-cursor check is the guard that stops a paging bug from quietly draining a balance overnight. Add a unique constraint on cid in your own storage and upsert rather than insert, because threads are re-ranked constantly and the same record will come back at a different position between runs.

Controlling the fan-out

Replies are where a comment job stops being cheap. Reading the top level is a handful of requests; expanding it is one request per parent comment, minimum. A video with 200 comments where 60 have replies costs 4 credits for the top level and at least 60 more for the second, and heavily answered threads need several pages each.

Three rules keep that in hand. Never call the endpoint when reply_comment_total is 0. Set a threshold - expanding only threads with three or more replies typically removes most of the calls while keeping most of the text. And rank what is left by digg_count or reply_score and expand the top slice, since a thread nobody engaged with rarely changes a conclusion. If the text is headed into classification afterwards, the notes on TikTok comment sentiment analysis cover what to do with it, and replies are worth including there - disagreement tends to live one level down.

It is also worth sanity-checking against the video itself. post-detail returns stats.commentCount, the figure TikTok reports for the post, which usually counts replies as well as top-level comments. Comparing it against your own total tells you how much of the discussion you actually reached. The guide to TikTok video data covers the rest of that record.

Credits, limits and errors

Every request costs 1 credit and the remaining balance comes back in the X-PrimeAPI-Balance response header. The default rate limit is 100 requests per minute, and reply collection is the workload most likely to hit it, because it produces many small requests in a burst - the short sleep in the script above is there for exactly that reason. Four failures come back as a JSON message rather than by status code alone: a missing key returns "Please sign up to primeapi.co", a wrong or inactive key returns "PrimeAPI-Key is not available", crossing the limit returns the per-minute message, and an empty balance returns "Your balance has been exhausted...". Branch on that body, not only on the HTTP status. The API documentation lists the same set in reference form, and the playground runs any endpoint against your own key before you write code.

Budgeting is easy once the threshold is set. New accounts get 50 free credits, which is enough to rebuild several complete threads end to end, and the paid tiers on the pricing page start at 2,500 credits for $9.90 and run to 500,000 for $279.00. If you have not created a key yet, registration takes about a minute.

Putting it together

Reply collection is a two-pass job with one decision in the middle. Pass one pages post-comments and keeps every cid with a non-zero reply_comment_total. The decision is which of those deserve a request, driven by reply count, likes and the ranking hints already in the payload. Pass two pages post-comment-replies for the survivors, and reply_id with reply_to_reply_id reassembles the tree locally without a single extra call. Guard against null arrays, cap your pages, watch the balance header, and a full TikTok discussion becomes something you can reconstruct on demand instead of reading off a screen.

Frequently asked questions

What do I need to call post-comment-replies?

Two required parameters: postId, the numeric ID of the video, and commentId, the ID of the parent comment. count and cursor are optional and control paging. Both IDs are required together, because a comment ID on its own does not tell the endpoint which video it belongs to.

Where does the commentId come from?

From the cid field of a record returned by post-comments. There is no separate lookup: you page the top level of the thread first, keep the cid of every comment whose reply_comment_total is greater than zero, and use those values as commentId. If you already store comments in your own database, read the IDs from there and skip the harvest step.

How deep does the TikTok comment tree go?

Two levels. A reply to a reply is still returned inside the same parent thread rather than as a third level, and reply_to_reply_id records which reply was being answered. When it is "0" the message answers the top-level comment directly, so you can rebuild the visual nesting from that one field without ever calling the endpoint recursively.

How many credits does expanding a whole thread cost?

One credit per request at every level. A video with 200 top-level comments read 50 at a time is 4 credits, and each comment you then expand costs at least 1 more. Expanding 60 of those comments takes the run to roughly 64 credits. Filtering on reply_comment_total before you fan out is what keeps that number predictable.

Why is reply_comment null on reply records?

Because replies do not nest a preview of their own. On a top-level comment reply_comment can carry a short inline sample of the thread, which is useful enough that a comment with exactly one reply often needs no second call at all. On the replies endpoint the field is always null, and thread_has_more plus the envelope has_more tell you whether there is more to page.

Are replies fetched in real time?

Yes, with no caching, so a reply posted a moment ago can show up in your next call. Average response time is around one second and the default rate limit is 100 requests per minute, which matters here because reply collection fans out into many small requests rather than a few large ones.

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