{
  "openapi": "3.0.1",
  "info": {
    "title": "TikTok Comments Scraper - Comments, Authors & Likes",
    "description": "Scrape comments from TikTok videos using one or more video URLs or IDs. Extract comment text, author, likes, reply count and timestamp in clean structured data. Supports multiple videos in a single run with no login required.",
    "version": "0.1",
    "x-build-id": "jAWn9VW5mHDA1ek7q"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~tiktok-comments-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-tiktok-comments-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/abotapi~tiktok-comments-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-tiktok-comments-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/abotapi~tiktok-comments-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-tiktok-comments-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "videos",
              "search"
            ],
            "type": "string",
            "description": "How to choose the videos. 'videos': scrape the video URLs/ids you paste. 'search': give usernames and the actor walks their recent videos, then scrapes comments on those.",
            "default": "videos"
          },
          "videoUrls": {
            "title": "Video URLs or IDs",
            "type": "array",
            "description": "One or more TikTok videos to pull comments from: full video URLs (with or without a query string), m.tiktok.com/v/<id>.html links, or numeric video ids. Several URLs of the same video are scraped once. Short share links (vm.tiktok.com) are not supported.",
            "default": [
              "https://www.tiktok.com/@tiktok/video/7673909736131038495",
              "https://www.tiktok.com/@scout2015/video/6718335390845095173"
            ],
            "items": {
              "type": "string"
            }
          },
          "usernames": {
            "title": "Usernames",
            "type": "array",
            "description": "One or more TikTok usernames (with or without @, or a pasted profile URL such as https://www.tiktok.com/@tiktok). The actor lists each account's recent videos and scrapes their comments.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxVideosPerUser": {
            "title": "Max videos per username (search mode)",
            "minimum": 0,
            "type": "integer",
            "description": "How many of each account's most recent videos to scan for comments. 0 = the maximum (10,000).",
            "default": 10
          },
          "profileSorting": {
            "title": "Order of an account's videos (search mode)",
            "enum": [
              "latest",
              "popular",
              "oldest"
            ],
            "type": "string",
            "description": "Which of each account's videos are scanned first: latest, most liked (popular) or oldest. popular and oldest rank the account's most recent 500 videos, so 'oldest' is the oldest of those.",
            "default": "latest"
          },
          "profileScrapeSections": {
            "title": "Account sections (search mode)",
            "type": "array",
            "description": "Which part of an account to scan. 'videos' is supported. 'reposts' is not exposed by the source: if it is the only entry, nothing is scanned for that account and a warning is logged.",
            "items": {
              "type": "string",
              "enum": [
                "videos",
                "reposts"
              ],
              "enumTitles": [
                "Videos",
                "Reposts (not available)"
              ]
            },
            "default": [
              "videos"
            ]
          },
          "oldestPostDateUnified": {
            "title": "Only videos posted after (search mode)",
            "type": "string",
            "description": "Skip videos older than this. A date (YYYY-MM-DD) or an age such as 30 or '2 weeks'. Empty = no limit."
          },
          "newestPostDate": {
            "title": "Only videos posted before (search mode)",
            "type": "string",
            "description": "Skip videos newer than this date (YYYY-MM-DD). Empty = no limit."
          },
          "excludePinnedPosts": {
            "title": "Skip pinned videos (search mode)",
            "type": "boolean",
            "description": "Leave out the videos the account pinned to the top of its profile.",
            "default": false
          },
          "minCommentLikes": {
            "title": "Minimum comment likes",
            "minimum": 0,
            "type": "integer",
            "description": "Only keep comments with at least this many likes. 0 = no minimum.",
            "default": 0
          },
          "commentKeyword": {
            "title": "Comment text contains",
            "type": "string",
            "description": "Only keep comments whose text contains this word or phrase (case-insensitive). Leave empty for no text filter."
          },
          "excludeReplies": {
            "title": "Exclude replies",
            "type": "boolean",
            "description": "Keep only top-level comments and drop replies to other comments.",
            "default": false
          },
          "commentsSinceDays": {
            "title": "Only comments from the last N days",
            "minimum": 0,
            "type": "integer",
            "description": "Drop comments older than this many days. 0 = no date filter.",
            "default": 0
          },
          "fetchReplies": {
            "title": "Fetch replies",
            "type": "boolean",
            "description": "Also try to collect replies. The source currently returns no reply rows for automated sessions, so this usually adds nothing; leave it off unless you want to try.",
            "default": false
          },
          "maxRepliesPerComment": {
            "title": "Max replies per comment",
            "minimum": 0,
            "type": "integer",
            "description": "Cap on replies collected per comment when reply fetching is enabled. 0 = the maximum (1,000).",
            "default": 5
          },
          "replyPatienceSeconds": {
            "title": "Seconds per reply page",
            "minimum": 3,
            "type": "integer",
            "description": "Time budget for each reply request. Only used when reply fetching is enabled.",
            "default": 10
          },
          "maxCommentsPerVideo": {
            "title": "Max comments per video",
            "minimum": 0,
            "type": "integer",
            "description": "How many comments to collect from each video. 0 = the maximum (100,000).",
            "default": 20
          },
          "topLevelCommentsPerPost": {
            "title": "Max top-level comments per video",
            "minimum": 0,
            "type": "integer",
            "description": "Stop after this many top-level comments per video that pass your filters; replies and filtered-out comments do not count toward it. 0 = no separate limit (the overall per-video limit still applies).",
            "default": 0
          },
          "maxPagesPerVideo": {
            "title": "Max pages per video",
            "minimum": 0,
            "type": "integer",
            "description": "Safety cap on result pages per video. 0 = no page limit (the comment cap above still applies).",
            "default": 0
          },
          "pagePatienceSeconds": {
            "title": "Seconds per page",
            "minimum": 5,
            "type": "integer",
            "description": "The source answers only a share of requests, and only during certain windows (sometimes shut for a minute or more), so each page keeps being requested until one succeeds or this budget runs out. Raise it for stubborn videos.",
            "default": 120
          },
          "maxAttemptsPerPage": {
            "title": "Max requests per page (optional ceiling)",
            "minimum": 0,
            "type": "integer",
            "description": "Optional hard ceiling on requests per page. 0 = no ceiling (the seconds budget above is the only limit).",
            "default": 0
          },
          "concurrency": {
            "title": "Parallel requests",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "How many requests run at the same time. Higher samples more exits per second. 20 is the maximum.",
            "default": 20
          },
          "proxyTier": {
            "title": "Proxy tier",
            "enum": [
              "datacenter",
              "residential"
            ],
            "type": "string",
            "description": "Measured 2026-10-15 through Apify Proxy, datacenter and residential both pass only a few percent of requests (1/40 vs 0/40), so datacenter stays the default; residential is a fallback, not an improvement.",
            "default": "datacenter"
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Leave apifyProxyGroups unset so the 'Proxy tier' field above decides. Set it here only to pin a non-standard group. Leave the country empty; exits rotate automatically."
          },
          "resumeFromRunId": {
            "title": "Resume from a previous run",
            "type": "string",
            "description": "Paste a previous run ID or dataset ID to continue a large crawl without re-collecting or re-billing comments already saved there."
          },
          "incrementalMode": {
            "title": "Incremental changes for scheduled runs",
            "type": "boolean",
            "description": "Turn this on for recurring monitoring: each comment is labelled NEW, UPDATED, UNCHANGED or REAPPEARED against the remembered baseline, and unchanged comments are not returned. A comment becomes UPDATED when ANY output field changes (text, diggCount, replyCommentTotal, and also author flags such as likedByAuthor or pinnedByAuthor, authorNickname, mentions, images), and changedFields names which. Only scrapedAt, authorAvatarUrl (a rotating picture link), avatarThumbnail, submittedVideoUrl, input and anything you list under Ignore fields are excluded. Turn on Emit unchanged or Emit expired only if you also want those rows returned (and billed).",
            "default": false
          },
          "stateKey": {
            "title": "State key (optional, incremental mode only)",
            "type": "string",
            "description": "Optional name for this monitoring campaign. Leave empty to let the actor derive a key from the videos and filters."
          },
          "emitUnchanged": {
            "title": "Emit unchanged comments (incremental mode only)",
            "type": "boolean",
            "description": "Return comments that did not change since the last run. Off by default because unchanged rows are usually noise.",
            "default": false
          },
          "emitExpired": {
            "title": "Emit expired comments (incremental mode only)",
            "type": "boolean",
            "description": "Return comments that were in the baseline but are gone this run. Only emitted after a run that provably finished the whole tracked scope.",
            "default": false
          },
          "ignoreFieldsForChanges": {
            "title": "Ignore fields when detecting changes (incremental mode only)",
            "type": "array",
            "description": "Optional. Top-level field names to leave out of UPDATED change detection, on top of the built-in exclusions (scrapedAt, authorAvatarUrl, a rotating signed picture link, avatarThumbnail, submittedVideoUrl and input never trigger a false UPDATED). Ignored fields stay in the output.",
            "items": {
              "type": "string"
            }
          },
          "mcpConnectors": {
            "title": "Pipe results into your apps (optional)",
            "type": "array",
            "description": "Optionally send results into the apps you already use, via Model Context Protocol connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write or digest. Leave empty to skip; never changes the dataset output."
          },
          "notionParentPageUrl": {
            "title": "Notion parent page (Notion connector only)",
            "type": "string",
            "description": "URL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors."
          },
          "maxNotifyListings": {
            "title": "Max items to export per connector",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Cap on items written to each connector per run. Does not affect the dataset.",
            "default": 50
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}