Skip to main content
Blog

How to scrape TikTok video comments

Use the tiktok comments scraper to extract public comments, replies, IDs, and like counts from any TikTok video URL.

ScrapersTikTokMay 26, 2026
How to scrape TikTok video comments

The TikTok comments scraper extracts public comments from a TikTok video URL. It returns comment text, likes, replies, commenter profile URL, commenter ID, and comment URL. Teams use it to pull comment datasets from specific videos while ScrapeNow handles TikTok selectors, sessions, retries, and anti-bot handling.

Use this scraper when you already have the TikTok video URL. If you need to discover videos first, start with a TikTok post search scraper, then pass the selected video URLs into this comments scraper.

How to use this scraper

TikTok Comments Scraper job pipeline
The TikTok Comments Scraper job pipeline, from input to stored output.

ScrapeNow’s Extract TikTok comments scraper takes one input field, url, and returns structured comment records.

The input must be a TikTok video URL that starts with https://www.tiktok.com/.

Step 1. Get the TikTok video URL

Open TikTok in your browser.

TikTok concert video playing with URL highlighted in address bar
Open the TikTok video and copy its URL from the address bar
Open the video you want to scrape. You can also search for the video and click the video result.

Copy the URL from the browser address bar.

TikTok video search results grid for taylor swift query
Paste the video URL into the scraper input field
The input should look like this:
{
  "url": "https://www.tiktok.com/@taylorswift/video/7582623625484848415"
}

If you need video metadata before pulling comments, run the Extract TikTok post data scraper against the same video URL. That gives you the post ID, author details, caption, engagement counts, and timestamps before you collect the comment thread.

Step 2. Run the scraper with the API

Create an output directory first. The script writes results to output/tiktok-comments-extract-by-url.json.

mkdir -p output

Use this exact Python script:

"""
Configuration:
    - Set SCRAPER_SLUG to the scraper you want to run.
    - Set SCRAPER_INPUTS to the list of input dicts matching that scraper's schema.
    - Set API_KEY to your scraper API key.
"""

import sys
import time
import json
import requests
import os

API_KEY = "YOUR_API_KEY"

SCRAPER_SLUG = "tiktok-comments-extract-by-url"

SCRAPER_INPUTS = [
    {
        "url": "https://www.tiktok.com/@taylorswift/video/7582623625484848415"
    }
]



BASE_URL = "https://api.scrapenow.io/api/v1/scraping"
TIMEOUT_SECONDS = 3600
POLL_INTERVAL = 5
SPINNER = "|/-\\"


def build_headers(api_key: str, content_type: str | None = None) -> dict:
    headers = {"Authorization": f"Bearer {api_key}"}
    if content_type:
        headers["Content-Type"] = content_type
    return headers


def trigger_scrape(slug: str, inputs: list[dict]) -> str:
    url = f"{BASE_URL}/scrape?scraper={slug}"
    response = requests.post(
        url,
        headers=build_headers(API_KEY, "application/json"),
        json={"inputs": inputs},
    )
    response.raise_for_status()
    return response.json()["data"]["job_id"]


def poll_until_done(job_id: str) -> str:
    start = time.time()
    i = 0
    while True:
        elapsed = time.time() - start
        if elapsed > TIMEOUT_SECONDS:
            print(f"\nTimeout after {TIMEOUT_SECONDS}s")
            sys.exit(1)
        response = requests.get(
            f"{BASE_URL}/jobs/{job_id}",
            headers=build_headers(API_KEY),
        )
        response.raise_for_status()
        data = response.json()
        status = data["data"]["status"]
        mins, secs = divmod(int(elapsed), 60)
        sys.stdout.write(
            f"\r[{SPINNER[i % 4]}] Waiting... {status} ({mins}m {secs:02d}s)  "
        )
        sys.stdout.flush()
        if status in ("completed", "failed"):
            print()
            return status
        time.sleep(POLL_INTERVAL)
        i += 1


def fetch_results(job_id: str) -> dict:
    response = requests.get(
        f"{BASE_URL}/jobs/{job_id}/results?format=json",
        headers=build_headers(API_KEY),
    )
    response.raise_for_status()
    return response.json()


def save_results(data: dict, slug: str) -> str:
    os.makedirs("output", exist_ok=True)
    filename = os.path.join("output", f"{slug}.json")
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
    return filename


def main() -> None:
    print(f"Triggering scraper: {SCRAPER_SLUG}")
    job_id = trigger_scrape(SCRAPER_SLUG, SCRAPER_INPUTS)
    print(f"Job started: {job_id}")
    final_status = poll_until_done(job_id)
    if final_status != "completed":
        print(f"Job failed with status: {final_status}")
        sys.exit(1)
    print("Fetching results...")
    results = fetch_results(job_id)
    output_file = save_results(results, SCRAPER_SLUG)
    print(f"Results saved to: {output_file}")


if __name__ == "__main__":
    main()

The script uses limit_per_input set to 1 for a small smoke test. Raise that value when you want more comments per video. Keep the lower value while testing authentication, job polling, and file output.

For multiple videos, add more input objects to SCRAPER_INPUTS. Each object needs its own url field.

SCRAPER_INPUTS = [
    {"url": "https://www.tiktok.com/@creator/video/1111111111111111111"},
    {"url": "https://www.tiktok.com/@creator/video/2222222222222222222"},
]

Step 3. Read the output JSON

The script starts a job, polls every 5 seconds, waits up to 3600 seconds, downloads the results as JSON, and saves them locally.

A trimmed response looks like this:

[
  {
    "inputs": {
      "url": "https://www.tiktok.com/@taylorswift/video/7582623625484848415"
    },
    "scrape_status": "success",
    "url": "https://www.tiktok.com/@taylorswift/video/7582623625484848415",
    "post_url": "https://www.tiktok.com/@taylorswift/video/7582623625484848415",
    "post_id": "7582623625484848415",
    "post_date_created": "2025-12-11T15:32:37.000Z",
    "date_created": "2026-01-21T10:07:18.000Z",
    "comment_text": "sorry kanye, you were right.",
    "num_likes": 2418,
    "num_replies": 45,
    "commenter_user_name": "mcsmooth",
    "commenter_id": "7245691966805148715",
    "commenter_url": "https://www.tiktok.com/@mcsmoothie",
    "comment_id": "7597753568250905374",
    "comment_url": "https://www.tiktok.com/@taylorswift/video/7582623625484848415?comment_id=7597753568250905374",
    "replies": null,
    "scrape_error": null,
    "scrape_error_code": null
  }
]

What data you get back, response fields that matter

TikTok Comments Scraper output schema
TikTok Comments Scraper output fields grouped by category.

Each row is one scraped comment from one TikTok video.

Field Type What it gives you
inputs.url string The video URL you submitted
scrape_status string success or a failed scrape state
post_url string Canonical TikTok video URL
post_id string TikTok video ID
post_date_created string Video creation timestamp
date_created string Comment creation timestamp
comment_text string Comment body
num_likes integer Like count on the comment
num_replies integer Reply count on the comment
commenter_user_name string Public TikTok username
commenter_id string TikTok user ID
commenter_url string Public profile URL for the commenter
comment_id string Stable comment ID
comment_url string Direct URL to the video with comment_id attached
replies object or null Reply payload when returned
scrape_error string or null Error message for failed rows
scrape_error_code string or null Machine-readable error code

Ready to get this data? Extract TikTok comments.

For storage, use comment_id as the primary key when you scrape one video.

Use (post_id, comment_id) as the primary key when you scrape many videos into the same table. TikTok IDs are strings. Do not cast them to integers in JavaScript, spreadsheets, or databases with fixed integer ranges.

This matters in JavaScript because large numeric IDs exceed Number.MAX_SAFE_INTEGER. A string ID stays exact. A numeric ID can lose precision and break joins, deduplication, and comment URL reconstruction.

Production tips, validation, deduplication, schema, error handling

Validate input before the API call, store raw output, dedupe on stable IDs, and retry failed rows without rerunning successful rows.

Validate input URLs before sending jobs

Reject short links, mobile app links, and empty strings before you call the API.

from urllib.parse import urlparse

def validate_tiktok_video_url(url: str) -> str:
    if not isinstance(url, str) or not url.strip():
        raise ValueError("url is required")

    cleaned = url.strip()
    parsed = urlparse(cleaned)

    if parsed.scheme != "https":
        raise ValueError("url must use https")

    if parsed.netloc != "www.tiktok.com":
        raise ValueError("url must start with https://www.tiktok.com/")

    if "/video/" not in parsed.path:
        raise ValueError("url must be a TikTok video URL")

    return cleaned


urls = [
    "https://www.tiktok.com/@taylorswift/video/7582623625484848415"
]

SCRAPER_INPUTS = [{"url": validate_tiktok_video_url(url)} for url in urls]

If you need to find videos first, use the Search TikTok posts by keyword scraper to build a video URL list. Then pass those URLs into the comments scraper.

Deduplicate on comment_id

Use comment_id for one-video jobs. Use (post_id, comment_id) for multi-video jobs.

import json

def load_rows(path: str) -> list[dict]:
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def dedupe_comments(rows: list[dict]) -> list[dict]:
    seen = set()
    clean = []

    for row in rows:
        post_id = row.get("post_id")
        comment_id = row.get("comment_id")

        if not post_id or not comment_id:
            continue

        key = (post_id, comment_id)
        if key in seen:
            continue

        seen.add(key)
        clean.append(row)

    return clean


rows = load_rows("output/tiktok-comments-extract-by-url.json")
deduped = dedupe_comments(rows)

print(f"Loaded {len(rows)} rows")
print(f"Kept {len(deduped)} unique rows")

Run deduplication before inserts and after retries.

Store IDs as text

Store post_id, comment_id, and commenter_id as TEXT. Do not store them as floats.

CREATE TABLE tiktok_comments (
    post_id TEXT NOT NULL,
    comment_id TEXT NOT NULL,
    post_url TEXT,
    comment_url TEXT,
    comment_text TEXT,
    date_created TIMESTAMP,
    post_date_created TIMESTAMP,
    num_likes INTEGER,
    num_replies INTEGER,
    commenter_user_name TEXT,
    commenter_id TEXT,
    commenter_url TEXT,
    scrape_status TEXT,
    scrape_error TEXT,
    scrape_error_code TEXT,
    raw_json JSONB,
    inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (post_id, comment_id)
);

Keep raw_json so you can backfill historical rows when you add a field later.

Handle failed rows without dropping the full job

Filter scrape_status, store failed rows, and retry only failed inputs.

def split_success_and_failed(rows: list[dict]) -> tuple[list[dict], list[dict]]:
    success = []
    failed = []

    for row in rows:
        if row.get("scrape_status") == "success" and not row.get("scrape_error"):
            success.append(row)
        else:
            failed.append(row)

    return success, failed


success_rows, failed_rows = split_success_and_failed(deduped)

print(f"Successful rows: {len(success_rows)}")
print(f"Failed rows: {len(failed_rows)}")

retry_inputs = []
for row in failed_rows:
    input_url = row.get("inputs", {}).get("url")
    if input_url:
        retry_inputs.append({"url": input_url})

print(retry_inputs)

Store scrape_error_code with the failed row to separate input problems from temporary scrape failures.

For profile-level jobs, collect profile URLs with the Get TikTok profile data scraper. Pull posts by profile URL, then extract comments from selected videos.

When to use the comments scraper with other TikTok scrapers

The comments scraper starts from a video URL. That works when you already know the posts you care about.

If you need discovery before comment extraction, use this flow:

Job Scraper
Find posts by query Search TikTok posts by keyword
Find posts from a creator Pull posts from a TikTok profile
Pull metadata for known videos Extract TikTok post data
Pull comments from known videos Extract TikTok comments

A common production path starts with keyword search, filters videos by engagement, then scrapes comments for the selected post URLs. Another path starts with creator profiles, pulls recent posts, and extracts comments from posts that pass your threshold.

The full TikTok scraper set lives under Browse all 86+ scrapers, covering 14 platforms.

Pricing

ScrapeNow charges per returned row. One row costs one credit, starting at $0.04 per credit for small runs and dropping with volume. No monthly contracts, no proxy fees, no charges for failed rows. See the pricing page for current rates.

Related articles

View all

Start collecting data in under five minutes.

Free credits included - no credit card required.

Free credits included - no credit card required