{
  "data": {
    "id": "tva5GT3U0Q1bbpOGj",
    "actId": "deOnMBknQfl9b7ZlY",
    "userId": "NL4ThgItAwaoBlffQ",
    "startedAt": "2026-10-17T06:21:54.726Z",
    "finishedAt": "2026-10-17T06:22:08.410Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 13592,
      "runTimeSecs": 13.592,
      "computeUnits": 0.015102222222222222,
      "imageSizeBytes": 318259712
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "zifX0mkAFMHe8CatY",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"TikTok Account Stats & Repost History Scraper Input\",\n  \"description\": \"Look up TikTok accounts and get their stats, full video history, reposts, playlists and live status. No cookies or proxy setup required.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"usernames\": {\n      \"title\": \"TikTok Usernames\",\n      \"type\": \"array\",\n      \"description\": \"TikTok usernames to look up (with or without @). You can mix usernames and profile URLs, and list as many as you need in one run.\",\n      \"editor\": \"stringList\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"prefill\": [\n        \"natgeo\"\n      ]\n    },\n    \"profileUrls\": {\n      \"title\": \"Profile URLs\",\n      \"type\": \"array\",\n      \"description\": \"Full TikTok profile URLs (e.g. https://www.tiktok.com/@khaby.lame). Can be used instead of or together with Usernames.\",\n      \"editor\": \"stringList\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"contentTypes\": {\n      \"title\": \"What to collect per account\",\n      \"type\": \"array\",\n      \"description\": \"Choose which rows to emit for every account. 'profile' = account stats and metadata, 'videos' = the account's posts, 'reposts' = content the account re-shared, 'playlists' = the account's playlists (names, covers, video counts).\",\n      \"editor\": \"select\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"profile\",\n          \"videos\",\n          \"reposts\",\n          \"playlists\"\n        ],\n        \"enumTitles\": [\n          \"Profile info and stats\",\n          \"Videos (full history)\",\n          \"Reposts\",\n          \"Playlists\"\n        ]\n      },\n      \"default\": [\n        \"profile\",\n        \"videos\"\n      ],\n      \"prefill\": [\n        \"profile\",\n        \"videos\",\n        \"reposts\"\n      ]\n    },\n    \"playlistUrls\": {\n      \"title\": \"Playlist URLs (optional)\",\n      \"type\": \"array\",\n      \"description\": \"Full playlist URLs (e.g. https://www.tiktok.com/@natgeo/playlist/wildlife-7509464647364283178) to collect the videos inside those playlists. Independent of Usernames.\",\n      \"editor\": \"stringList\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"maxPostsPerProfile\": {\n      \"title\": \"Max Post Rows per Profile\",\n      \"type\": \"integer\",\n      \"description\": \"Maximum number of post rows per account, combined across videos and reposts. 0 skips post collection entirely (useful for a fast stats-only run). Playlist rows are not counted.\",\n      \"default\": 30,\n      \"minimum\": 0,\n      \"maximum\": 500,\n      \"prefill\": 10\n    },\n    \"maxItems\": {\n      \"title\": \"Max Post Rows per Run\",\n      \"type\": \"integer\",\n      \"description\": \"Global cap on post rows for the whole run across all accounts (0 = no limit). Profile and playlist rows are not counted.\",\n      \"default\": 0,\n      \"minimum\": 0,\n      \"maximum\": 100000,\n      \"prefill\": 0\n    },\n    \"includeProfileInfo\": {\n      \"title\": \"Include Profile Info\",\n      \"type\": \"boolean\",\n      \"description\": \"When off, no profile rows are written even if 'profile' is selected above; only post rows are produced.\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"oldestPostDate\": {\n      \"title\": \"Only Posts From This Date (optional)\",\n      \"type\": \"string\",\n      \"description\": \"Skip posts published before this date (UTC). Format: YYYY-MM-DD, e.g. 2026-01-01. Leave empty to collect the newest posts with no lower bound.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"newestPostDate\": {\n      \"title\": \"Only Posts Up To This Date (optional)\",\n      \"type\": \"string\",\n      \"description\": \"Skip posts published after this date (UTC). Format: YYYY-MM-DD, e.g. 2026-01-01. Leave empty to start from each account's most recent post.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"includeLiveStatus\": {\n      \"title\": \"Include Live Status\",\n      \"type\": \"boolean\",\n      \"description\": \"Adds isLive and, when the account is live, the live room title, start time and viewer count to the profile row. Emitted only when TikTok reports the account's live status.\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"computedStats\": {\n      \"title\": \"Add Derived Statistics\",\n      \"type\": \"boolean\",\n      \"description\": \"Adds averagePlays, medianPlays and postsPerWeek to the profile row, computed over the videos fetched for that account in this run (the window is the returned set, not the full history).\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"onlyNewVideos\": {\n      \"title\": \"Only New Videos (monitoring)\",\n      \"type\": \"boolean\",\n      \"description\": \"When on, each account emits only the videos posted since the previous run of that same account. The newest video seen is remembered between runs in the actor's named key-value store, so scheduling the actor turns it into a new-video monitor. The first run for an account returns all videos up to Max Post Rows per Profile and seeds the state. Reposts and playlists are not filtered. If the stored state is ever lost, the next run behaves like a first run.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"pinnedFirst\": {\n      \"title\": \"Show Pinned Videos First\",\n      \"type\": \"boolean\",\n      \"description\": \"When on, videos the account pinned to the top of its profile (TikTok's pinned posts) are emitted before the rest of the post rows collected in this run. Pinned videos also carry isPinned: true.\",\n      \"default\": false,\n      \"prefill\": false\n    }\n  },\n  \"required\": []\n}",
    "readme": "# TikTok Account Stats & Repost History Scraper\n\nLook up any public TikTok account and export its stats, its public video history, the content it re-shared (reposts), its playlists and its current live status. Every account comes back as a profile row plus one row per video, repost or playlist, which makes it easy to schedule the actor and follow follower, like and posting trends over time. No TikTok account, cookies or proxy configuration are required.\n\n## What this actor does\n\n- **Account stats** — exact follower, following, hearts and video counts plus display name, bio, bio link, verification, business category, avatars and account settings.\n- **Public video history** — walks an account's posts newest-first, up to the limit you set, instead of stopping at a fixed handful of recent posts.\n- **Reposts** — the videos an account re-shared from other creators, with the original author preserved.\n- **Playlists** — playlist names, covers, video counts and links; add individual playlist URLs to collect the videos inside them.\n- **Live status** — whether the account is streaming right now, with the live room title, start time and viewer count when TikTok reports them.\n- **Filters and limits** — a date window, a global row cap, a per-account row cap and optional derived statistics (average plays, median plays, posts per week).\n- **New-video monitoring** — an `onlyNewVideos` mode schedules the actor as a change feed: it remembers each account's newest video between runs and emits only the videos posted since.\n- **Pinned posts** — pinned videos carry `isPinned: true` and, with `pinnedFirst`, are emitted before the rest of the collected posts.\n- **Stable play links** — every post row includes an `embedUrl` that plays the post in TikTok's embedded player without cookies, even after the session-bound media URLs expire.\n- **Batch friendly** — mix usernames and profile URLs in one run; duplicates are removed automatically.\n- **Empty fields are omitted** — no nulls, empty strings or placeholder values anywhere.\n\n## Output per profile row\n\nProfile rows have `rowType: \"profile\"`:\n\n- `userId`, `secUid`, `username`, `displayName`\n- `verified`, `privateAccount`, `isOrganization`, `ttSeller`\n- `bio`, `bioLink`, `bioEmail` (when a valid address is written in the bio)\n- `language`, `commerceCategory`\n- `followerCount`, `followingCount`, `heartCount`, `videoCount`, `diggCount` — exact counts, never rounded abbreviations\n- `avatarLarger`, `avatarMedium`, `avatarThumb`\n- `createTime`, `createdAt`\n- `commentSetting`, `duetSetting`, `stitchSetting`, `downloadSetting`, `followingVisibility`, `openFavorite`, `profileEmbedPermission`, `profileTab`, `UserStoryStatus`\n- `isLive`, `liveRoomId`, `liveRoomTitle`, `liveStartTime`, `liveViewerCount` — present when TikTok reports the account's live status\n- `averagePlays`, `medianPlays`, `postsPerWeek` — only with \"Add Derived Statistics\" enabled, computed over the videos fetched for that account in the run\n- `profileUrl`, `scrapedAt`\n\n## Output per video or repost row\n\nPost rows have `rowType: \"video\"` for the account's own posts and `rowType: \"repost\"` for re-shared content, with `contentKind` mirroring the same value:\n\n- `profileUsername` — the account the row was collected from\n- `postId`, `postUrl`, `embedUrl` (permanent cookieless player link), `postType` (`video` or `slideshow`), `caption`, `textLanguage`, `locationCreated`\n- `createTime`, `createdAt`\n- `playCount`, `likeCount`, `commentCount`, `shareCount`, `collectCount`, `repostCount`\n- `isPinned` — present when the account pinned this video to the top of its profile\n- `stitchEnabled`, `shareEnabled`, `duetEnabled`, `commentStatus`\n- `isAd`, `isAigc`, `isECVideo`, `privateItem`, `forFriend`, `originalItem`, `adLabelVersion`, `AIGCDescription`\n- `author.*` — author snapshot at collection time: `id`, `secUid`, `username`, `displayName`, `bio`, `verified`, `privateAccount`, `roomId`, `avatarUrl`, `followerCount`, `followingCount`, `heartCount`, `videoCount`\n- `music.*` — sound used: `id`, `title`, `authorName`, `original`, `duration`, `album`, `isCopyrighted`, `isCommerceMusic`, `isUnlimitedMusic`, `isPrivate`, `shootDuration`, `playUrl`, `coverUrl`, `musicUrl`, `dspLinks`\n- `video.*` — technical metadata and media links: `width`, `height`, `duration`, `ratio`, `definition`, `bitrate`, `codecType`, `format`, `size`, `videoQuality`, `hasSubtitles`, `subtitleInfos`, `bitrateInfo`, `volumeInfo`, `playUrl`, `downloadAddr`, `cover`, `originCover`, `dynamicCover`\n- `images` — image list for slideshow posts\n- `hashtags`, `mentions`\n- `mediaUrlExpiresAt` — when the signed media links expire, when TikTok provides it\n- `stickersOnItem`, `effectStickers`, `diversificationLabels`, `channelTags`, `item_control`, `contents`, `poi`, `warnInfo` — extra post metadata, present when TikTok supplies it\n- `sourcePlaylistId` — for videos collected from playlist URLs\n\n## Output per playlist row\n\nPlaylist rows have `rowType: \"playlist\"`:\n\n- `profileUsername` — the account that owns the playlist\n- `playlistId`, `mixId`, `name`, `videoCount`\n- `coverUrl`, `playlistUrl`, `scrapedAt`\n\n## Input\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `usernames` | array | – | TikTok usernames with or without `@` |\n| `profileUrls` | array | – | Full profile URLs, usable instead of or alongside usernames |\n| `contentTypes` | array | `[\"profile\",\"videos\"]` | Any combination of `profile`, `videos`, `reposts`, `playlists` |\n| `playlistUrls` | array | – | Playlist URLs to collect the videos inside them |\n| `maxPostsPerProfile` | integer | `30` | Max post rows per account, combined across videos and reposts (0 skips posts) |\n| `maxItems` | integer | `0` | Global cap on post rows for the whole run (0 = no cap) |\n| `includeProfileInfo` | boolean | `true` | Set to false to emit post rows only |\n| `oldestPostDate` | string | – | Only posts published on or after this date (`YYYY-MM-DD`, UTC) |\n| `newestPostDate` | string | – | Only posts published on or before this date (`YYYY-MM-DD`, UTC) |\n| `includeLiveStatus` | boolean | `true` | Adds live status fields to profile rows when TikTok reports them |\n| `computedStats` | boolean | `false` | Adds average plays, median plays and posts per week to profile rows |\n| `onlyNewVideos` | boolean | `false` | Monitoring mode: emit only videos posted since the previous run of the same account |\n| `pinnedFirst` | boolean | `false` | Emit pinned videos before the rest of the post rows |\n\n### Example: stats snapshot for a watchlist\n\n```json\n{\n  \"usernames\": [\"nike\", \"adidas\", \"puma\"],\n  \"contentTypes\": [\"profile\"],\n  \"maxPostsPerProfile\": 0,\n  \"computedStats\": true\n}\n```\n\n### Example: recent videos and reposts since a date\n\n```json\n{\n  \"usernames\": [\"natgeo\", \"bbcnews\"],\n  \"contentTypes\": [\"profile\", \"videos\", \"reposts\"],\n  \"maxPostsPerProfile\": 50,\n  \"oldestPostDate\": \"2026-09-01\"\n}\n```\n\n### Example: repost history only\n\n```json\n{\n  \"usernames\": [\"khaby.lame\"],\n  \"contentTypes\": [\"reposts\"],\n  \"maxPostsPerProfile\": 100\n}\n```\n\n### Example: collect the videos inside a playlist\n\n```json\n{\n  \"playlistUrls\": [\"https://www.tiktok.com/@natgeo/playlist/Cinematic-7509464647364283178\"],\n  \"maxPostsPerProfile\": 100\n}\n```\n\n### Example: full history for a short list\n\n```json\n{\n  \"usernames\": [\"corgibobaa\"],\n  \"contentTypes\": [\"videos\"],\n  \"maxPostsPerProfile\": 500,\n  \"maxItems\": 500\n}\n```\n\n### Example: new-video monitoring\n\n```json\n{\n  \"usernames\": [\"natgeo\", \"bbcnews\"],\n  \"contentTypes\": [\"profile\", \"videos\"],\n  \"maxPostsPerProfile\": 50,\n  \"onlyNewVideos\": true\n}\n```\n\nSchedule this input daily. The first run returns the account's videos (up to the cap) and remembers the newest one; every later run returns only videos posted since the previous run, so you get a clean new-video feed without duplicates. Reposts and playlists are not filtered by this option.\n\n## Use cases\n\n- **Influencer vetting** — pull follower counts, engagement counters and recent performance for a shortlist of creators.\n- **Follower growth tracking** — schedule a stats-only run to build a time series of followers, hearts and video counts.\n- **Competitor monitoring** — watch posting cadence and engagement for brand accounts with date-windowed runs.\n- **Repost analysis** — see exactly which outside content a creator chooses to amplify.\n- **Playlist research** — export curated playlists as structured video lists.\n- **Live campaign checks** — confirm whether an account is live and capture the stream details in the same run.\n\n## FAQ\n\n### Do I need a TikTok account, cookies or a proxy?\n\nNo. The actor reads public profile data exactly as any logged-out visitor could. There is nothing to configure beyond your input list. (Older versions accepted `sessionCookie`, `cookies` and `ttwid`; those fields were removed because they never changed what the actor returns — old saved tasks that still send them keep working unchanged.)\n\n### How does onlyNewVideos behave on the first run?\n\nThe first run for an account returns all videos it collects (up to Max Post Rows per Profile) and stores the newest video as the monitoring checkpoint. Every following run returns only videos newer than that checkpoint. If the stored state is ever lost, the next run simply behaves like a first run again — you never get an error.\n\n### Why did a monitoring run return no video rows?\n\nEither the account posted nothing since the previous run, or the checkpoint already covers its newest video. The run still succeeds and still emits the profile row, so a scheduled stats-only watch keeps working.\n\n### How far back does the video history go?\n\nAs far as you ask it to. `maxPostsPerProfile` caps the rows per account, and date filters let you stop at a chosen day. Large accounts can be paged to hundreds of videos; very large request sizes simply take longer.\n\n### Why does an account show no reposts?\n\nMany accounts genuinely never repost. When an account has no publicly visible reposts, the run still succeeds and simply emits no repost rows.\n\n### Why can reposts be missing when I ask for both videos and reposts?\n\n`maxPostsPerProfile` is a combined cap and videos are written first, so a video-heavy account can use the whole budget on videos. Choose `reposts` only if repost history is what you need.\n\n### Why are some fields missing from a row?\n\nEmpty fields are omitted on purpose. Some TikTok accounts do not publish a bio link, a location, subtitles or hashtags, and some payloads do not carry every optional flag — those fields are left out rather than filled with blanks.\n\n### What do averagePlays and medianPlays cover?\n\nThey are computed over the videos returned for that account in the same run, not over the account's entire history. Fetch more videos for a wider window.\n\n### Do the video and audio links work in my browser?\n\nThe `video.playUrl`, `video.downloadAddr` and `music.playUrl` links are short-lived addresses that TikTok binds to the session that requested them; opening them later usually returns Access Denied. They are kept because existing integrations reference them, but `embedUrl` is the durable, cookieless way to play a post: it is a permanent TikTok embed link that opens the post in TikTok's own player in any browser, with no account or cookies. `postUrl` remains the canonical link to the post page, and the image links (`cover`, `originCover`, avatars, playlist covers) resolve normally.\n\n### Which videos does pinnedFirst reorder?\n\nTikTok lets an account pin up to three videos to the top of its profile. When `pinnedFirst` is on, any pinned video collected in the run is emitted before the other post rows, and it carries `isPinned: true`. Pinned videos normally appear inside their chronological position in TikTok's own API, so without this option they keep that position.\n\n### What happens to private, suspended or misspelled accounts?\n\nThey are skipped: no rows are written for them, the run still succeeds and the status message reports how many accounts could not be found. Profile stats for private accounts are returned when TikTok makes them public.\n\n### Is the account location or country included?\n\nTikTok no longer includes an account region in its public profile payload, so the actor does not emit one.\n\n### Can I track changes over time?\n\nYes. Schedule the actor with a stats-only input and compare `followerCount`, `heartCount` and `videoCount` between runs using `scrapedAt` as the timestamp.\n\n### Is this affiliated with TikTok?\n\nNo. It is a third-party actor that reads publicly available TikTok web data.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "tiktok-account-stats-and-repost-history-scraper",
      "title": "TikTok Account Stats & Repost History Scraper",
      "description": "Look up TikTok accounts for exact follower, like and video stats, the full public video history, reposted content, playlists and live status. Every run emits account rows plus video, repost and playlist rows — no cookies, logins or proxy setup.",
      "version": "1.0",
      "input": {
        "title": "TikTok Account Stats & Repost History Scraper Input",
        "description": "Look up TikTok accounts and get their stats, full video history, reposts, playlists and live status. No cookies or proxy setup required.",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "usernames": {
            "title": "TikTok Usernames",
            "type": "array",
            "description": "TikTok usernames to look up (with or without @). You can mix usernames and profile URLs, and list as many as you need in one run.",
            "editor": "stringList",
            "items": {
              "type": "string"
            },
            "prefill": [
              "natgeo"
            ]
          },
          "profileUrls": {
            "title": "Profile URLs",
            "type": "array",
            "description": "Full TikTok profile URLs (e.g. https://www.tiktok.com/@khaby.lame). Can be used instead of or together with Usernames.",
            "editor": "stringList",
            "items": {
              "type": "string"
            }
          },
          "contentTypes": {
            "title": "What to collect per account",
            "type": "array",
            "description": "Choose which rows to emit for every account. 'profile' = account stats and metadata, 'videos' = the account's posts, 'reposts' = content the account re-shared, 'playlists' = the account's playlists (names, covers, video counts).",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "profile",
                "videos",
                "reposts",
                "playlists"
              ],
              "enumTitles": [
                "Profile info and stats",
                "Videos (full history)",
                "Reposts",
                "Playlists"
              ]
            },
            "default": [
              "profile",
              "videos"
            ],
            "prefill": [
              "profile",
              "videos",
              "reposts"
            ]
          },
          "playlistUrls": {
            "title": "Playlist URLs (optional)",
            "type": "array",
            "description": "Full playlist URLs (e.g. https://www.tiktok.com/@natgeo/playlist/wildlife-7509464647364283178) to collect the videos inside those playlists. Independent of Usernames.",
            "editor": "stringList",
            "items": {
              "type": "string"
            }
          },
          "maxPostsPerProfile": {
            "title": "Max Post Rows per Profile",
            "type": "integer",
            "description": "Maximum number of post rows per account, combined across videos and reposts. 0 skips post collection entirely (useful for a fast stats-only run). Playlist rows are not counted.",
            "default": 30,
            "minimum": 0,
            "maximum": 500,
            "prefill": 10
          },
          "maxItems": {
            "title": "Max Post Rows per Run",
            "type": "integer",
            "description": "Global cap on post rows for the whole run across all accounts (0 = no limit). Profile and playlist rows are not counted.",
            "default": 0,
            "minimum": 0,
            "maximum": 100000,
            "prefill": 0
          },
          "includeProfileInfo": {
            "title": "Include Profile Info",
            "type": "boolean",
            "description": "When off, no profile rows are written even if 'profile' is selected above; only post rows are produced.",
            "default": true,
            "prefill": true
          },
          "oldestPostDate": {
            "title": "Only Posts From This Date (optional)",
            "type": "string",
            "description": "Skip posts published before this date (UTC). Format: YYYY-MM-DD, e.g. 2026-01-01. Leave empty to collect the newest posts with no lower bound.",
            "editor": "textfield",
            "default": "",
            "prefill": ""
          },
          "newestPostDate": {
            "title": "Only Posts Up To This Date (optional)",
            "type": "string",
            "description": "Skip posts published after this date (UTC). Format: YYYY-MM-DD, e.g. 2026-01-01. Leave empty to start from each account's most recent post.",
            "editor": "textfield",
            "default": "",
            "prefill": ""
          },
          "includeLiveStatus": {
            "title": "Include Live Status",
            "type": "boolean",
            "description": "Adds isLive and, when the account is live, the live room title, start time and viewer count to the profile row. Emitted only when TikTok reports the account's live status.",
            "default": true,
            "prefill": true
          },
          "computedStats": {
            "title": "Add Derived Statistics",
            "type": "boolean",
            "description": "Adds averagePlays, medianPlays and postsPerWeek to the profile row, computed over the videos fetched for that account in this run (the window is the returned set, not the full history).",
            "default": false,
            "prefill": false
          },
          "onlyNewVideos": {
            "title": "Only New Videos (monitoring)",
            "type": "boolean",
            "description": "When on, each account emits only the videos posted since the previous run of that same account. The newest video seen is remembered between runs in the actor's named key-value store, so scheduling the actor turns it into a new-video monitor. The first run for an account returns all videos up to Max Post Rows per Profile and seeds the state. Reposts and playlists are not filtered. If the stored state is ever lost, the next run behaves like a first run.",
            "default": false,
            "prefill": false
          },
          "pinnedFirst": {
            "title": "Show Pinned Videos First",
            "type": "boolean",
            "description": "When on, videos the account pinned to the top of its profile (TikTok's pinned posts) are emitted before the rest of the post rows collected in this run. Pinned videos also carry isPinned: true.",
            "default": false,
            "prefill": false
          }
        },
        "required": []
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "TikTok Account Stats & Repost History Scraper Output",
        "description": "Account rows with exact stats and live status, video and repost rows with engagement metrics and media metadata, and playlist rows for the account's playlists.",
        "properties": {
          "data": {
            "type": "string",
            "title": "Accounts, Posts & Playlists",
            "description": "Dataset rows with a rowType discriminator: 'profile' for account stats, 'video'/'repost' for posts, 'playlist' for playlists.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "dockerfile": "./Dockerfile",
      "readme": "# TikTok Account Stats & Repost History Scraper\n\nLook up any public TikTok account and export its stats, its public video history, the content it re-shared (reposts), its playlists and its current live status. Every account comes back as a profile row plus one row per video, repost or playlist, which makes it easy to schedule the actor and follow follower, like and posting trends over time. No TikTok account, cookies or proxy configuration are required.\n\n## What this actor does\n\n- **Account stats** — exact follower, following, hearts and video counts plus display name, bio, bio link, verification, business category, avatars and account settings.\n- **Public video history** — walks an account's posts newest-first, up to the limit you set, instead of stopping at a fixed handful of recent posts.\n- **Reposts** — the videos an account re-shared from other creators, with the original author preserved.\n- **Playlists** — playlist names, covers, video counts and links; add individual playlist URLs to collect the videos inside them.\n- **Live status** — whether the account is streaming right now, with the live room title, start time and viewer count when TikTok reports them.\n- **Filters and limits** — a date window, a global row cap, a per-account row cap and optional derived statistics (average plays, median plays, posts per week).\n- **New-video monitoring** — an `onlyNewVideos` mode schedules the actor as a change feed: it remembers each account's newest video between runs and emits only the videos posted since.\n- **Pinned posts** — pinned videos carry `isPinned: true` and, with `pinnedFirst`, are emitted before the rest of the collected posts.\n- **Stable play links** — every post row includes an `embedUrl` that plays the post in TikTok's embedded player without cookies, even after the session-bound media URLs expire.\n- **Batch friendly** — mix usernames and profile URLs in one run; duplicates are removed automatically.\n- **Empty fields are omitted** — no nulls, empty strings or placeholder values anywhere.\n\n## Output per profile row\n\nProfile rows have `rowType: \"profile\"`:\n\n- `userId`, `secUid`, `username`, `displayName`\n- `verified`, `privateAccount`, `isOrganization`, `ttSeller`\n- `bio`, `bioLink`, `bioEmail` (when a valid address is written in the bio)\n- `language`, `commerceCategory`\n- `followerCount`, `followingCount`, `heartCount`, `videoCount`, `diggCount` — exact counts, never rounded abbreviations\n- `avatarLarger`, `avatarMedium`, `avatarThumb`\n- `createTime`, `createdAt`\n- `commentSetting`, `duetSetting`, `stitchSetting`, `downloadSetting`, `followingVisibility`, `openFavorite`, `profileEmbedPermission`, `profileTab`, `UserStoryStatus`\n- `isLive`, `liveRoomId`, `liveRoomTitle`, `liveStartTime`, `liveViewerCount` — present when TikTok reports the account's live status\n- `averagePlays`, `medianPlays`, `postsPerWeek` — only with \"Add Derived Statistics\" enabled, computed over the videos fetched for that account in the run\n- `profileUrl`, `scrapedAt`\n\n## Output per video or repost row\n\nPost rows have `rowType: \"video\"` for the account's own posts and `rowType: \"repost\"` for re-shared content, with `contentKind` mirroring the same value:\n\n- `profileUsername` — the account the row was collected from\n- `postId`, `postUrl`, `embedUrl` (permanent cookieless player link), `postType` (`video` or `slideshow`), `caption`, `textLanguage`, `locationCreated`\n- `createTime`, `createdAt`\n- `playCount`, `likeCount`, `commentCount`, `shareCount`, `collectCount`, `repostCount`\n- `isPinned` — present when the account pinned this video to the top of its profile\n- `stitchEnabled`, `shareEnabled`, `duetEnabled`, `commentStatus`\n- `isAd`, `isAigc`, `isECVideo`, `privateItem`, `forFriend`, `originalItem`, `adLabelVersion`, `AIGCDescription`\n- `author.*` — author snapshot at collection time: `id`, `secUid`, `username`, `displayName`, `bio`, `verified`, `privateAccount`, `roomId`, `avatarUrl`, `followerCount`, `followingCount`, `heartCount`, `videoCount`\n- `music.*` — sound used: `id`, `title`, `authorName`, `original`, `duration`, `album`, `isCopyrighted`, `isCommerceMusic`, `isUnlimitedMusic`, `isPrivate`, `shootDuration`, `playUrl`, `coverUrl`, `musicUrl`, `dspLinks`\n- `video.*` — technical metadata and media links: `width`, `height`, `duration`, `ratio`, `definition`, `bitrate`, `codecType`, `format`, `size`, `videoQuality`, `hasSubtitles`, `subtitleInfos`, `bitrateInfo`, `volumeInfo`, `playUrl`, `downloadAddr`, `cover`, `originCover`, `dynamicCover`\n- `images` — image list for slideshow posts\n- `hashtags`, `mentions`\n- `mediaUrlExpiresAt` — when the signed media links expire, when TikTok provides it\n- `stickersOnItem`, `effectStickers`, `diversificationLabels`, `channelTags`, `item_control`, `contents`, `poi`, `warnInfo` — extra post metadata, present when TikTok supplies it\n- `sourcePlaylistId` — for videos collected from playlist URLs\n\n## Output per playlist row\n\nPlaylist rows have `rowType: \"playlist\"`:\n\n- `profileUsername` — the account that owns the playlist\n- `playlistId`, `mixId`, `name`, `videoCount`\n- `coverUrl`, `playlistUrl`, `scrapedAt`\n\n## Input\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `usernames` | array | – | TikTok usernames with or without `@` |\n| `profileUrls` | array | – | Full profile URLs, usable instead of or alongside usernames |\n| `contentTypes` | array | `[\"profile\",\"videos\"]` | Any combination of `profile`, `videos`, `reposts`, `playlists` |\n| `playlistUrls` | array | – | Playlist URLs to collect the videos inside them |\n| `maxPostsPerProfile` | integer | `30` | Max post rows per account, combined across videos and reposts (0 skips posts) |\n| `maxItems` | integer | `0` | Global cap on post rows for the whole run (0 = no cap) |\n| `includeProfileInfo` | boolean | `true` | Set to false to emit post rows only |\n| `oldestPostDate` | string | – | Only posts published on or after this date (`YYYY-MM-DD`, UTC) |\n| `newestPostDate` | string | – | Only posts published on or before this date (`YYYY-MM-DD`, UTC) |\n| `includeLiveStatus` | boolean | `true` | Adds live status fields to profile rows when TikTok reports them |\n| `computedStats` | boolean | `false` | Adds average plays, median plays and posts per week to profile rows |\n| `onlyNewVideos` | boolean | `false` | Monitoring mode: emit only videos posted since the previous run of the same account |\n| `pinnedFirst` | boolean | `false` | Emit pinned videos before the rest of the post rows |\n\n### Example: stats snapshot for a watchlist\n\n```json\n{\n  \"usernames\": [\"nike\", \"adidas\", \"puma\"],\n  \"contentTypes\": [\"profile\"],\n  \"maxPostsPerProfile\": 0,\n  \"computedStats\": true\n}\n```\n\n### Example: recent videos and reposts since a date\n\n```json\n{\n  \"usernames\": [\"natgeo\", \"bbcnews\"],\n  \"contentTypes\": [\"profile\", \"videos\", \"reposts\"],\n  \"maxPostsPerProfile\": 50,\n  \"oldestPostDate\": \"2026-09-01\"\n}\n```\n\n### Example: repost history only\n\n```json\n{\n  \"usernames\": [\"khaby.lame\"],\n  \"contentTypes\": [\"reposts\"],\n  \"maxPostsPerProfile\": 100\n}\n```\n\n### Example: collect the videos inside a playlist\n\n```json\n{\n  \"playlistUrls\": [\"https://www.tiktok.com/@natgeo/playlist/Cinematic-7509464647364283178\"],\n  \"maxPostsPerProfile\": 100\n}\n```\n\n### Example: full history for a short list\n\n```json\n{\n  \"usernames\": [\"corgibobaa\"],\n  \"contentTypes\": [\"videos\"],\n  \"maxPostsPerProfile\": 500,\n  \"maxItems\": 500\n}\n```\n\n### Example: new-video monitoring\n\n```json\n{\n  \"usernames\": [\"natgeo\", \"bbcnews\"],\n  \"contentTypes\": [\"profile\", \"videos\"],\n  \"maxPostsPerProfile\": 50,\n  \"onlyNewVideos\": true\n}\n```\n\nSchedule this input daily. The first run returns the account's videos (up to the cap) and remembers the newest one; every later run returns only videos posted since the previous run, so you get a clean new-video feed without duplicates. Reposts and playlists are not filtered by this option.\n\n## Use cases\n\n- **Influencer vetting** — pull follower counts, engagement counters and recent performance for a shortlist of creators.\n- **Follower growth tracking** — schedule a stats-only run to build a time series of followers, hearts and video counts.\n- **Competitor monitoring** — watch posting cadence and engagement for brand accounts with date-windowed runs.\n- **Repost analysis** — see exactly which outside content a creator chooses to amplify.\n- **Playlist research** — export curated playlists as structured video lists.\n- **Live campaign checks** — confirm whether an account is live and capture the stream details in the same run.\n\n## FAQ\n\n### Do I need a TikTok account, cookies or a proxy?\n\nNo. The actor reads public profile data exactly as any logged-out visitor could. There is nothing to configure beyond your input list. (Older versions accepted `sessionCookie`, `cookies` and `ttwid`; those fields were removed because they never changed what the actor returns — old saved tasks that still send them keep working unchanged.)\n\n### How does onlyNewVideos behave on the first run?\n\nThe first run for an account returns all videos it collects (up to Max Post Rows per Profile) and stores the newest video as the monitoring checkpoint. Every following run returns only videos newer than that checkpoint. If the stored state is ever lost, the next run simply behaves like a first run again — you never get an error.\n\n### Why did a monitoring run return no video rows?\n\nEither the account posted nothing since the previous run, or the checkpoint already covers its newest video. The run still succeeds and still emits the profile row, so a scheduled stats-only watch keeps working.\n\n### How far back does the video history go?\n\nAs far as you ask it to. `maxPostsPerProfile` caps the rows per account, and date filters let you stop at a chosen day. Large accounts can be paged to hundreds of videos; very large request sizes simply take longer.\n\n### Why does an account show no reposts?\n\nMany accounts genuinely never repost. When an account has no publicly visible reposts, the run still succeeds and simply emits no repost rows.\n\n### Why can reposts be missing when I ask for both videos and reposts?\n\n`maxPostsPerProfile` is a combined cap and videos are written first, so a video-heavy account can use the whole budget on videos. Choose `reposts` only if repost history is what you need.\n\n### Why are some fields missing from a row?\n\nEmpty fields are omitted on purpose. Some TikTok accounts do not publish a bio link, a location, subtitles or hashtags, and some payloads do not carry every optional flag — those fields are left out rather than filled with blanks.\n\n### What do averagePlays and medianPlays cover?\n\nThey are computed over the videos returned for that account in the same run, not over the account's entire history. Fetch more videos for a wider window.\n\n### Do the video and audio links work in my browser?\n\nThe `video.playUrl`, `video.downloadAddr` and `music.playUrl` links are short-lived addresses that TikTok binds to the session that requested them; opening them later usually returns Access Denied. They are kept because existing integrations reference them, but `embedUrl` is the durable, cookieless way to play a post: it is a permanent TikTok embed link that opens the post in TikTok's own player in any browser, with no account or cookies. `postUrl` remains the canonical link to the post page, and the image links (`cover`, `originCover`, avatars, playlist covers) resolve normally.\n\n### Which videos does pinnedFirst reorder?\n\nTikTok lets an account pin up to three videos to the top of its profile. When `pinnedFirst` is on, any pinned video collected in the run is emitted before the other post rows, and it carries `isPinned: true`. Pinned videos normally appear inside their chronological position in TikTok's own API, so without this option they keep that position.\n\n### What happens to private, suspended or misspelled accounts?\n\nThey are skipped: no rows are written for them, the run still succeeds and the status message reports how many accounts could not be found. Profile stats for private accounts are returned when TikTok makes them public.\n\n### Is the account location or country included?\n\nTikTok no longer includes an account region in its public profile payload, so the actor does not emit one.\n\n### Can I track changes over time?\n\nYes. Schedule the actor with a stats-only input and compare `followerCount`, `heartCount` and `videoCount` between runs using `scrapedAt` as the timestamp.\n\n### Is this affiliated with TikTok?\n\nNo. It is a third-party actor that reads publicly available TikTok web data.\n",
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "title": "TikTok Profiles and Posts",
          "views": {
            "overview": {
              "title": "Profiles Overview",
              "description": "TikTok profile rows (rowType='profile') and post rows (rowType='video' or 'repost') in a single dataset.",
              "transformation": {
                "fields": [
                  "rowType",
                  "username",
                  "displayName",
                  "followerCount",
                  "videoCount",
                  "profileUrl",
                  "scrapedAt"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "rowType": {
                    "label": "Row Type",
                    "format": "text"
                  },
                  "username": {
                    "label": "Username",
                    "format": "text"
                  },
                  "displayName": {
                    "label": "Display Name",
                    "format": "text"
                  },
                  "followerCount": {
                    "label": "Followers",
                    "format": "number"
                  },
                  "videoCount": {
                    "label": "Videos",
                    "format": "number"
                  },
                  "profileUrl": {
                    "label": "Profile URL",
                    "format": "link"
                  },
                  "scrapedAt": {
                    "label": "Scraped At",
                    "format": "text"
                  }
                }
              }
            }
          },
          "fields": {
            "rowType": {
              "type": "string",
              "title": "Row Type",
              "description": "Discriminator field: 'profile' for profile rows, 'video' for video post rows, 'repost' for reposted post rows, 'playlist' for playlist metadata rows"
            },
            "userId": {
              "type": "string",
              "title": "User ID",
              "description": "TikTok internal user ID (string to preserve 64-bit precision). Present on profile rows."
            },
            "secUid": {
              "type": "string",
              "title": "Secure UID",
              "description": "TikTok secure user identifier used in API calls. Present on profile rows."
            },
            "username": {
              "type": "string",
              "title": "Username",
              "description": "TikTok username (handle) without the @ prefix. Present on profile rows."
            },
            "displayName": {
              "type": "string",
              "title": "Display Name",
              "description": "User's public display name / nickname. Present on profile rows."
            },
            "bio": {
              "type": "string",
              "title": "Bio",
              "description": "Profile biography / signature text. Present on profile rows."
            },
            "bioLink": {
              "type": "string",
              "title": "Bio Link",
              "description": "URL in the profile bio link field. Present on profile rows when set."
            },
            "verified": {
              "type": "boolean",
              "title": "Verified",
              "description": "Whether the account has TikTok's verified badge. Present on profile rows."
            },
            "privateAccount": {
              "type": "boolean",
              "title": "Private Account",
              "description": "Whether the account is set to private. Present on profile rows."
            },
            "language": {
              "type": "string",
              "title": "Language",
              "description": "Account's primary language setting. Present on profile rows when available."
            },
            "isOrganization": {
              "type": "boolean",
              "title": "Is Organization",
              "description": "Whether the account is registered as an organization. Present on profile rows."
            },
            "ttSeller": {
              "type": "boolean",
              "title": "TT Seller",
              "description": "Whether the account is a TikTok Shop seller. Present on profile rows."
            },
            "roomId": {
              "type": "string",
              "title": "Room ID",
              "description": "Live room ID — non-empty when the user is currently streaming. Present on profile rows when live."
            },
            "createTime": {
              "type": "integer",
              "title": "Create Time (Unix)",
              "description": "Unix timestamp when the post was created. Present on post rows."
            },
            "createdAt": {
              "type": "string",
              "title": "Created At",
              "description": "ISO 8601 UTC datetime when the post was created. Present on post rows."
            },
            "avatarLarger": {
              "type": "string",
              "title": "Avatar (Large)",
              "description": "URL of the large avatar image. Present on profile rows."
            },
            "avatarMedium": {
              "type": "string",
              "title": "Avatar (Medium)",
              "description": "URL of the medium avatar image. Present on profile rows."
            },
            "avatarThumb": {
              "type": "string",
              "title": "Avatar (Thumbnail)",
              "description": "URL of the thumbnail avatar image. Present on profile rows."
            },
            "followerCount": {
              "type": "integer",
              "title": "Follower Count",
              "description": "Number of followers (from statsV2). Present on profile rows."
            },
            "followingCount": {
              "type": "integer",
              "title": "Following Count",
              "description": "Number of accounts followed (from statsV2). Present on profile rows."
            },
            "heartCount": {
              "type": "integer",
              "title": "Heart Count",
              "description": "Total likes/hearts received across all posts (from statsV2). Present on profile rows."
            },
            "videoCount": {
              "type": "integer",
              "title": "Video Count",
              "description": "Number of public videos posted (from statsV2). Present on profile rows."
            },
            "diggCount": {
              "type": "integer",
              "title": "Digg Count",
              "description": "Number of posts liked by this user (from statsV2). Present on profile rows when available."
            },
            "commentSetting": {
              "type": "integer",
              "title": "Comment Setting",
              "description": "Who can comment on the user's posts (0=everyone, 1=friends, 2=no one). Present on profile rows."
            },
            "duetSetting": {
              "type": "integer",
              "title": "Duet Setting",
              "description": "Who can duet the user's videos. Present on profile rows."
            },
            "stitchSetting": {
              "type": "integer",
              "title": "Stitch Setting",
              "description": "Who can stitch the user's videos. Present on profile rows."
            },
            "downloadSetting": {
              "type": "integer",
              "title": "Download Setting",
              "description": "Whether others can download the user's videos. Present on profile rows."
            },
            "followingVisibility": {
              "type": "integer",
              "title": "Following Visibility",
              "description": "Who can see the user's following list (0=everyone, 1=friends, 2=no one). Present on profile rows."
            },
            "openFavorite": {
              "type": "boolean",
              "title": "Open Favorite",
              "description": "Whether the user's liked videos are publicly visible. Present on profile rows."
            },
            "profileEmbedPermission": {
              "type": "integer",
              "title": "Profile Embed Permission",
              "description": "Whether the user allows profile embedding. Present on profile rows."
            },
            "profileTab": {
              "type": "object",
              "title": "Profile Tab",
              "description": "Visibility settings for profile tabs (music, questions, playlists). Present on profile rows.",
              "properties": {
                "showMusicTab": {
                  "type": "boolean"
                },
                "showQuestionTab": {
                  "type": "boolean"
                },
                "showPlayListTab": {
                  "type": "boolean"
                }
              }
            },
            "UserStoryStatus": {
              "type": "integer",
              "title": "User Story Status",
              "description": "TikTok Stories status flag. Present on profile rows when available."
            },
            "profileUrl": {
              "type": "string",
              "title": "Profile URL",
              "description": "Direct TikTok profile URL (https://www.tiktok.com/@username). Present on profile rows."
            },
            "scrapedAt": {
              "type": "string",
              "title": "Scraped At",
              "description": "ISO 8601 UTC datetime when the profile was scraped. Present on profile rows."
            },
            "profileUsername": {
              "type": "string",
              "title": "Profile Username",
              "description": "Username of the profile that was scraped — links post rows back to their source profile. Present on post rows."
            },
            "contentKind": {
              "type": "string",
              "title": "Content Kind",
              "description": "Whether the post is the user's own content ('video') or a repost ('repost'). Present on post rows."
            },
            "postId": {
              "type": "string",
              "title": "Post ID",
              "description": "TikTok's unique post/video ID (string to preserve 64-bit precision). Present on post rows."
            },
            "postUrl": {
              "type": "string",
              "title": "Post URL",
              "description": "Direct URL to the TikTok post. Present on post rows."
            },
            "embedUrl": {
              "type": "string",
              "title": "Embed URL",
              "description": "Permanent, cookieless player link for the post (https://www.tiktok.com/embed/v2/<postId>). Opens the post in TikTok's embedded player in any browser with no account or cookies."
            },
            "postType": {
              "type": "string",
              "title": "Post Type",
              "description": "Either 'video' or 'slideshow'. Present on post rows."
            },
            "caption": {
              "type": "string",
              "title": "Caption",
              "description": "Post caption/description text including hashtags and mentions. Present on post rows."
            },
            "textLanguage": {
              "type": "string",
              "title": "Text Language",
              "description": "Detected language code of the caption text. Present on post rows when available."
            },
            "locationCreated": {
              "type": "string",
              "title": "Location Created",
              "description": "ISO country code where the post was created. Present on post rows when available."
            },
            "playCount": {
              "type": "integer",
              "title": "Play Count",
              "description": "Number of video plays. Present on post rows."
            },
            "likeCount": {
              "type": "integer",
              "title": "Like Count",
              "description": "Number of likes/diggs. Present on post rows."
            },
            "commentCount": {
              "type": "integer",
              "title": "Comment Count",
              "description": "Number of comments. Present on post rows."
            },
            "shareCount": {
              "type": "integer",
              "title": "Share Count",
              "description": "Number of shares. Present on post rows."
            },
            "collectCount": {
              "type": "integer",
              "title": "Collect Count",
              "description": "Number of times post was bookmarked/collected. Present on post rows."
            },
            "repostCount": {
              "type": "integer",
              "title": "Repost Count",
              "description": "Number of reposts. Present on post rows when available."
            },
            "stitchEnabled": {
              "type": "boolean",
              "title": "Stitch Enabled",
              "description": "Whether stitching is allowed for this post. Present on post rows."
            },
            "shareEnabled": {
              "type": "boolean",
              "title": "Share Enabled",
              "description": "Whether sharing is enabled for this post. Present on post rows."
            },
            "duetEnabled": {
              "type": "boolean",
              "title": "Duet Enabled",
              "description": "Whether duets are allowed for this post. Present on post rows."
            },
            "isAd": {
              "type": "boolean",
              "title": "Is Ad",
              "description": "Whether this post is a paid advertisement. Present on post rows."
            },
            "isAigc": {
              "type": "boolean",
              "title": "Is AI Generated Content",
              "description": "Whether TikTok labels this as AI-generated content. Present on post rows."
            },
            "isECVideo": {
              "type": "boolean",
              "title": "Is E-Commerce Video",
              "description": "Whether this is a TikTok Shop / e-commerce video. Present on post rows."
            },
            "privateItem": {
              "type": "boolean",
              "title": "Private Item",
              "description": "Whether the post is set to private. Present on post rows."
            },
            "author": {
              "type": "object",
              "title": "Author",
              "description": "Author snapshot at time of scraping. Present on post rows.",
              "properties": {
                "id": {
                  "type": "string",
                  "title": "User ID"
                },
                "secUid": {
                  "type": "string",
                  "title": "Secure UID"
                },
                "username": {
                  "type": "string",
                  "title": "Username"
                },
                "displayName": {
                  "type": "string",
                  "title": "Display Name"
                },
                "bio": {
                  "type": "string",
                  "title": "Bio"
                },
                "verified": {
                  "type": "boolean",
                  "title": "Verified"
                },
                "privateAccount": {
                  "type": "boolean",
                  "title": "Private Account"
                },
                "roomId": {
                  "type": "string",
                  "title": "Live Room ID"
                },
                "avatarUrl": {
                  "type": "string",
                  "title": "Avatar URL"
                },
                "followerCount": {
                  "type": "integer",
                  "title": "Followers"
                },
                "followingCount": {
                  "type": "integer",
                  "title": "Following"
                },
                "heartCount": {
                  "type": "integer",
                  "title": "Hearts"
                },
                "videoCount": {
                  "type": "integer",
                  "title": "Videos"
                }
              }
            },
            "music": {
              "type": "object",
              "title": "Music",
              "description": "Sound/music used in the post. Present on post rows. Note: playUrl is a signed CDN URL bound to the IP/session that fetched it and may return HTTP 403 if opened from a different network (same behavior as video.playUrl/downloadAddr).",
              "properties": {
                "id": {
                  "type": "string",
                  "title": "Music ID"
                },
                "title": {
                  "type": "string",
                  "title": "Title"
                },
                "authorName": {
                  "type": "string",
                  "title": "Author"
                },
                "original": {
                  "type": "boolean",
                  "title": "Original Sound"
                },
                "duration": {
                  "type": "integer",
                  "title": "Duration (s)"
                },
                "isCopyrighted": {
                  "type": "boolean",
                  "title": "Is Copyrighted"
                },
                "isCommerceMusic": {
                  "type": "boolean",
                  "title": "Is Commerce Music"
                },
                "isUnlimitedMusic": {
                  "type": "boolean",
                  "title": "Is Unlimited Music"
                },
                "isPrivate": {
                  "type": "boolean",
                  "title": "Is Private Sound"
                },
                "shootDuration": {
                  "type": "integer",
                  "title": "Shoot Duration (s)"
                },
                "album": {
                  "type": "string",
                  "title": "Album"
                },
                "playUrl": {
                  "type": "string",
                  "title": "Play URL"
                },
                "coverUrl": {
                  "type": "string",
                  "title": "Cover URL"
                },
                "musicUrl": {
                  "type": "string",
                  "title": "Music URL"
                },
                "dspLinks": {
                  "type": "array",
                  "title": "DSP Links",
                  "description": "Links to the sound on external music platforms (Spotify, Apple Music, etc.) when TikTok provides them."
                }
              }
            },
            "video": {
              "type": "object",
              "title": "Video",
              "description": "Video technical metadata and signed play/cover URLs. Present on post rows. Note: playUrl/downloadAddr are bound to the IP/session that fetched them and may return HTTP 403 if opened from a different network.",
              "properties": {
                "width": {
                  "type": "integer",
                  "title": "Width (px)"
                },
                "height": {
                  "type": "integer",
                  "title": "Height (px)"
                },
                "duration": {
                  "type": "integer",
                  "title": "Duration (s)"
                },
                "ratio": {
                  "type": "string",
                  "title": "Aspect Ratio"
                },
                "definition": {
                  "type": "string",
                  "title": "Definition"
                },
                "bitrate": {
                  "type": "integer",
                  "title": "Bitrate"
                },
                "codecType": {
                  "type": "string",
                  "title": "Codec"
                },
                "format": {
                  "type": "string",
                  "title": "Format"
                },
                "size": {
                  "type": "integer",
                  "title": "File Size (bytes)"
                },
                "playUrl": {
                  "type": "string",
                  "title": "Play URL"
                },
                "downloadAddr": {
                  "type": "string",
                  "title": "Download URL"
                },
                "cover": {
                  "type": "string",
                  "title": "Cover Image URL"
                },
                "originCover": {
                  "type": "string",
                  "title": "Original Cover URL"
                },
                "dynamicCover": {
                  "type": "string",
                  "title": "Dynamic Cover URL"
                },
                "videoQuality": {
                  "type": "string",
                  "title": "Video Quality Label"
                },
                "hasSubtitles": {
                  "type": "boolean",
                  "title": "Has Subtitles",
                  "description": "Whether TikTok-generated or creator-uploaded captions/subtitles are available for this video."
                },
                "subtitleInfos": {
                  "type": "array",
                  "title": "Subtitle Info",
                  "description": "Available caption/subtitle tracks (language, format, signed URL) when hasSubtitles is true."
                },
                "bitrateInfo": {
                  "type": "array",
                  "title": "Bitrate Info",
                  "description": "Available quality/bitrate variants for this video, each with its own signed play URL."
                },
                "volumeInfo": {
                  "type": "object",
                  "title": "Volume Info",
                  "description": "Loudness/peak audio metadata reported by TikTok for this video."
                }
              }
            },
            "images": {
              "type": "array",
              "title": "Slideshow Images",
              "description": "Images for slideshow posts (postType='slideshow'). Present on post rows when applicable.",
              "items": {
                "type": "object",
                "properties": {
                  "ordinal": {
                    "type": "integer",
                    "title": "Index"
                  },
                  "width": {
                    "type": "integer",
                    "title": "Width"
                  },
                  "height": {
                    "type": "integer",
                    "title": "Height"
                  },
                  "urlList": {
                    "type": "array",
                    "title": "Image URLs"
                  }
                }
              }
            },
            "hashtags": {
              "type": "array",
              "title": "Hashtags",
              "description": "Hashtags used in the post caption. Present on post rows when present.",
              "items": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "title": "Hashtag ID"
                  },
                  "name": {
                    "type": "string",
                    "title": "Hashtag Name"
                  },
                  "isCommerce": {
                    "type": "boolean",
                    "title": "Is Commerce"
                  }
                }
              }
            },
            "mentions": {
              "type": "array",
              "title": "Mentions",
              "description": "@mentions in the post caption. Present on post rows when present.",
              "items": {
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string",
                    "title": "Username"
                  },
                  "start": {
                    "type": "integer",
                    "title": "Start Offset"
                  },
                  "end": {
                    "type": "integer",
                    "title": "End Offset"
                  },
                  "awemeId": {
                    "type": "string",
                    "title": "Mentioned Post ID"
                  }
                }
              }
            },
            "mediaUrlExpiresAt": {
              "type": "string",
              "title": "Media URL Expires At",
              "description": "ISO 8601 UTC datetime when signed media URLs (video/cover) expire. Present on post rows when signed URLs are present."
            },
            "commentStatus": {
              "type": "integer",
              "title": "Comment Status",
              "description": "Raw TikTok comment-permission code for this post (0=everyone, 1=friends, 2=off, 3=followers). Present on post rows when available."
            },
            "adLabelVersion": {
              "type": "integer",
              "title": "Ad Label Version",
              "description": "Internal TikTok advertising-label version applied to this post. Present on post rows when the post carries an ad label."
            },
            "forFriend": {
              "type": "boolean",
              "title": "For Friends Only",
              "description": "Whether the post was published as friends-only. Present on post rows."
            },
            "originalItem": {
              "type": "boolean",
              "title": "Original Item",
              "description": "Whether TikTok flags this post as original (non-duplicated) content. Present on post rows."
            },
            "viewerRegion": {
              "type": "string",
              "title": "Viewer Region",
              "description": "ISO country code of the region the post metadata was served for. Present on post rows when available."
            },
            "AIGCDescription": {
              "type": "string",
              "title": "AI-Generated Content Description",
              "description": "Creator-provided disclosure text describing which parts of the post are AI-generated. Present on post rows when disclosed."
            },
            "stickersOnItem": {
              "type": "array",
              "title": "Stickers On Item",
              "description": "Interactive stickers (polls, Q&A, etc.) overlaid on the post, with their text and position data. Present on post rows when the post has stickers."
            },
            "effectStickers": {
              "type": "array",
              "title": "Effect Stickers",
              "description": "Visual effects/filters applied to the post. Present on post rows when effects were used."
            },
            "diversificationLabels": {
              "type": "array",
              "title": "Diversification Labels",
              "description": "TikTok content-category labels used for feed diversification. Present on post rows when available."
            },
            "channelTags": {
              "type": "array",
              "title": "Channel Tags",
              "description": "Content channel/category tags TikTok assigned to the post. Present on post rows when available."
            },
            "item_control": {
              "type": "object",
              "title": "Item Control",
              "description": "TikTok content-control flags (e.g. duet/stitch/download restrictions at the raw API level). Present on post rows when available."
            },
            "contents": {
              "type": "array",
              "title": "Contents",
              "description": "Structured caption segments (text/hashtag/mention parts) as returned by TikTok's content API. Present on post rows when available."
            },
            "poi": {
              "type": "object",
              "title": "Point of Interest",
              "description": "Tagged location/point-of-interest details (name, address, city) attached to the post. Present on post rows when the creator tagged a location."
            },
            "warnInfo": {
              "type": "array",
              "title": "Warning Info",
              "description": "Content warnings TikTok attaches to the post (e.g. sensitive content notices). Present on post rows when TikTok applies a warning."
            },
            "isLive": {
              "type": "boolean",
              "title": "Live now",
              "description": "True when TikTok reports the account as currently streaming. Present on profile rows when live status is resolved (omitted for accounts TikTok does not report)."
            },
            "liveRoomId": {
              "type": "string",
              "title": "Live room ID",
              "description": "TikTok live room identifier while the account is streaming. Present on profile rows when live."
            },
            "liveRoomTitle": {
              "type": "string",
              "title": "Live title",
              "description": "Title of the current live stream. Present on profile rows while the account is live."
            },
            "liveStartTime": {
              "type": "integer",
              "title": "Live start time",
              "description": "Unix timestamp (seconds) when the current live stream started."
            },
            "liveViewerCount": {
              "type": "integer",
              "title": "Live viewers",
              "description": "Number of viewers in the current live stream, when TikTok reports it."
            },
            "commerceCategory": {
              "type": "string",
              "title": "Business category",
              "description": "TikTok business account category (e.g. 'Media & Entertainment') when the account has one."
            },
            "bioEmail": {
              "type": "string",
              "title": "Email in bio",
              "description": "Email address found in the profile bio text, when one is present."
            },
            "averagePlays": {
              "type": "number",
              "title": "Average plays",
              "description": "Mean play count over the videos fetched for this account in this run (enabled by computedStats)."
            },
            "medianPlays": {
              "type": "number",
              "title": "Median plays",
              "description": "Median play count over the videos fetched for this account in this run (enabled by computedStats)."
            },
            "postsPerWeek": {
              "type": "number",
              "title": "Posts per week",
              "description": "Posting frequency implied by the createTime span of the fetched videos (enabled by computedStats)."
            },
            "playlistId": {
              "type": "string",
              "title": "Playlist ID",
              "description": "TikTok playlist identifier. Present on playlist rows."
            },
            "mixId": {
              "type": "string",
              "title": "Mix ID",
              "description": "TikTok mix identifier used for paginating playlist videos. Present on playlist rows."
            },
            "name": {
              "type": "string",
              "title": "Playlist name",
              "description": "Playlist display name. Present on playlist rows."
            },
            "coverUrl": {
              "type": "string",
              "title": "Cover URL",
              "description": "Signed cover image URL for the playlist or media entity."
            },
            "playlistUrl": {
              "type": "string",
              "title": "Playlist URL",
              "description": "Canonical TikTok URL of the playlist."
            },
            "sourcePlaylistId": {
              "type": "string",
              "title": "Source playlist ID",
              "description": "Playlist the video was collected from when it was fetched via playlistUrls."
            },
            "isPinned": {
              "type": "boolean",
              "title": "Pinned",
              "description": "True when the account pinned this video to the top of its profile. Only emitted for pinned videos."
            }
          }
        }
      }
    },
    "buildNumber": "1.0.2",
    "imageDigest": "6889c1388ab5e632eca4071ab9cdbd309aa6dd1e0b70ccace7457c3cb5abd194",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.015102222222222222
    }
  }
}