Scrape TikTok profile data at scale — followers and following lists, video commenters, and username enrichment. Every profile returns emails, bio links, follower and like counts, and video stats. No login needed. Export to CSV, JSON or Excel, or run via API.
All notable changes to the TikTok Profile Scraper actor are documented here.
The format follows Keep a Changelog .
[0.0.175] - 2026-08-18
Fixed
A person's name in a username field is no longer split into two unrelated accounts. A space
used to separate entries, so Resee Stevens was read as two usernames — Resee and Stevens —
and both exist on TikTok, belong to other people, and were analyzed and charged for. Entries are
now separated by commas and line breaks only. An entry containing a space is named as a name,
skipped, and never fetched. Pasting a comma-separated list into a single row still works exactly
as before.
A TikTok link that is not a profile is no longer fetched as if it were one.tiktok.com/video/…
and tiktok.com/tag/… used to be read as accounts called video and tag. Written without
https://, such a link was read as an account called tiktok.com. Both are now recognised and
reported instead of being looked up.
An entry containing ? or # is no longer quietly trimmed into a different account.who?!
used to become who, which is a real account and not the one you asked for. It is now reported as
an entry that cannot be a username.
Runs happening at the same time no longer share progress with each other. The record of which
accounts a run had already handled was kept for the whole actor rather than per run, so two runs
going at once could read each other's — one could skip accounts you had paid for, or wipe the
other's progress mid-run. Each run now keeps its own.
A restarted run no longer forgets its budget or its results. When a run moved to another server
it used to start its spending back at zero and could lose up to the last fifty rows it had gathered
but not yet written. It now saves everything it has collected before the move, and picks up where
it left off with its spending, its counts and its progress intact. Its final summary describes the
whole run rather than only the part after the restart.
A run that searched and found nobody no longer calls itself completed successfully. Its stop
reason now says that no accounts were found, the same way an unreachable list already did.
[0.0.174] - 2026-08-17
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.173. Saved tasks continue to work identically.
[0.0.173] - 2026-08-17
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.172. Saved tasks continue to work identically.
[0.0.172] - 2026-08-13
Fixed
A list whose entries are all unusable no longer reports itself as an empty list. It used to say
the field "came through empty" — sending you to check a field you had in fact filled in. It now
names the entries it could not use, explains what a username looks like, and its stop reason says
the input was the problem.
[0.0.171] - 2026-08-13
Fixed
A username that arrives with a link stuck to it is now read correctly. If a username reached the
actor joined onto its own profile address — for example name followed immediately by https: —
the run took the whole thing as the username, found no such account, and ended after about a second
reporting that no accounts could be analyzed. The username is now recovered from it and analyzed
normally. Sending a plain username or a full profile link on its own worked before and still does.
A username that cannot be used is now named. The closing message used to say only that the
accounts could not be found, which left you to work out which entry was at fault — and an entry
with a link stuck to it looks correct at a glance. Unusable entries are now listed by name, with a
note on what a username may contain.
A run that reached none of its accounts no longer calls itself completed successfully. Its stop
reason now says what actually happened. The same correction applies to a run whose filters rejected
every profile.
[0.0.170] - 2026-08-10
Changed
The rules that keep a supplier's name out of run output are now identical across all our actors. No
user-visible change here — this build only brings this actor in line with the shared copy.
[0.0.169] - 2026-08-10
Fixed
A failed run no longer prints internal technical detail from the service this actor depends on.
When a run failed because that service refused or stopped answering, the run log and the run's
RUN_SUMMARY record carried its raw reply — including its own name and its wording for the
problem. None of it was ever useful to you, and none of it belongs in a run log. A failed run now
says the failure was on our side, and nothing more.
Added
region — the run's region, previously only settable under an older key. The old key still
works, so nothing you have saved changes.
[0.0.168] - 2026-08-10
Changed
The free-plan limit ids listed in the run's FREE_LIMITS_APPLIED storage record now use one
shared vocabulary across all our actors, so the same limit reads the same wherever you meet it.
The human-readable message on each entry is unchanged.
[0.0.167] - 2026-07-29
Changed
The Store listing now says what this actor returns — emails, bio links, follower and like
counts and video stats on every profile, across follower and following lists, username
enrichment, and the accounts that commented on a video — and the price line shows the
per-profile price instead of a generic label.
[0.0.166] - 2026-07-28
Fixed
The record that lets an interrupted run resume is now declared in the actor's storage schema.
It was always written, but the schema did not list it, so anything reading the schema had no way
to know it existed.
Changed
Local helper files that were never needed at run time no longer travel with the actor, so the
build is smaller.
[0.0.165] - 2026-07-28
Maintenance build — no user-facing changes.
[0.0.164] - 2026-07-27
Added
New Source column on every row. It says how each profile entered your results — Follower of <target> or Following of <target> in Mode 1, Direct Input in Mode 2, Commenter on <target> in Mode 3. Previously, a run covering several target accounts produced one undifferentiated list and there was no way to tell which account any given profile came from. This column was already being worked out internally on every run; it simply was never written to the output.
Mode 3 rows now carry Comments Left and Posts Engaged.Comments Left counts every comment the account left across the target posts that were read, and Posts Engaged counts how many different posts they commented on — which separates someone engaging steadily from someone leaving a burst of replies under a single post. Both describe what the run actually read: scanning stops once your profile limit is reached, so treat them as a floor rather than a lifetime total.
Changed
Mode 3 results are now ordered by engagement instead of follower count — most comments first, then most posts engaged, with follower count as the tie-breaker. Follower count is the wrong headline for a mode whose purpose is finding the people who actually engage. Rows are written in batches and ordered within each batch, so sort the exported dataset by Comments Left if you need a strict ranking across the entire run.
In Mode 3, an account that comments on several of your target accounts is now counted across all of them, and its Source lists each one. Previously it was credited only to whichever target happened to be scanned first, and repeat comments from the same person were discarded rather than counted.
No pricing change: the same single Profile Analyzed event fires once per analyzed profile, exactly as before, and no run performs extra retrievals because of this release.
[0.0.163] - 2026-07-06
Changed
Every "Upgrade" link in run messages now says what's behind it: paid plans start at $29/mo with $29 in platform credits, and the link opens your plan page in Apify Console.
[0.0.162] - 2026-07-06
Fixed
The "Upgrade" link in run summaries now opens your Apify plan page directly (the old link form could land on the wrong billing tab).
[0.0.161] - 2026-07-02
Fixed
Profiles that fail to retrieve are no longer billed. Not-found, deleted, and private accounts never counted as deliverable rows, but each retrieval attempt was still charged as a Profile Analyzed event. Billing now fires only after a profile actually comes back readable — matching what the pricing table and FAQ always promised.
Trying the actor with the default demo input is now always cheap. The pre-filled example input (e.g. willsmith in Mode 1 with no profile cap) previously started a full-size run — on a paid account that meant an unbounded follower crawl. Runs using the untouched demo input are now detected and capped at 10 profiles (~$0.02), with a storage notice explaining the cap and how to switch to your own accounts.
Added
Every empty run now explains itself. A run that ends with 0 rows writes a USER_MESSAGE storage record naming the cause and the fix: no candidates found (per-mode guidance with known-good starter accounts), every profile rejected by your filters (with the top rejection reasons and which filter to loosen), or no account reachable (not-found / private / temporary-error breakdown). Previously these runs reported only "Completed successfully".
Free-plan caps now say what was held back — with real numbers. When the free plan trims your run (a list longer than 100 profiles, more than 3 target accounts, or the 100-profiles-per-run ceiling), the run writes a notice with the exact counts (e.g. "Your list has 110 profiles — the free plan analyzed the first 100") plus a FREE_LIMITS_APPLIED storage record. Caps you set yourself (like maxCountList) are never presented as plan limits.
Input form: honest guidance where it was missing — Mode 1 states it works on public accounts with visible lists only, the filter section explains that filters run after profiles are fetched, and username fields point to verified starter accounts.
Changed
README: mode-selection guidance in four places (each mode reads its own username field), an expectation line for Mode 1, three new FAQ entries (no-login/no-cookies, scheduled runs, "how do I get more results"), one-click example tasks, and a store description without pricing boilerplate.
[0.0.160] - 2026-05-07
Changed
README — aligned to Apify quality template. Hero rewritten with value-first prose and a "beyond what TikTok's official Research API offers" comparison; numbered 3-step Quick start; new 💡 Tips & Best Practices section (4 sub-sections × 3 bullets); new 🛟 Support & feedback section pointing to Apify Store reviews / bookmark / Issues tab; promoted disclaimer to dedicated ⚖️ Is it legal to scrape TikTok? H2 with link to Apify's web-scraping legality blog; FAQ expanded to 12 Q&As (including legality, ban risk, data freshness, cost cap); 3-mode descriptions condensed to a table. No functional changes — same input, output, dataset shape, and pricing.
[0.0.159] - 2026-05-05
Fixed
Run-level analytics no longer fail with "Insufficient permissions for the key-value store" on every paid run — the per-user session and first-run timestamp stores were using actor-agnostic names that, under Apify's LIMITED_PERMISSIONS scoping, were owned by another actor in the same account. Renamed to actor-scoped names so this actor's runs can read/write its own analytics state.
Changed
Improved diagnostics when every account in the input was already analyzed by an earlier run. Previously, the actor silently skipped every cached account, completed with 0 saved profiles, and reported "Completed successfully" — paying users saw a successful run that produced nothing. Runs in this state now show Stop Reason: All accounts already processed and write a USER_MESSAGE storage record telling you to set "Clear saved data before run" to true if you want to re-analyze them.
Per-account "skipped because already analyzed in a prior run" is now counted and surfaced in the run-complete summary block.
[0.0.4] - 2026-05-04
Changed
README expanded with a full output sample, pricing math examples, three filter recipes (nano-influencer outreach / English-speaking creators / B2B partnership shortlist via engaged commenters), an FAQ section, and a comparison table to other actors in this portfolio for multi-platform users.
Every output column now has a per-field description, type, and example available in the Apify Console's dataset view and to clients that read the actor schema programmatically — covers all 16 base columns plus the conditional Email, Website, and Post N columns.
Input field help text rewritten throughout — each field now leads with what it does and any prerequisites (e.g. "Required when keywordLocation is 'posts' or 'anywhere'"), so it's clearer what to set before running.
SEO title / description tightened on the Apify Store page (no functional change).
[0.0.3] - 2026-04-30
Fixed
Followup to 0.0.2: extended the bounded timeout to the remaining mid-run storage calls — the totals-counter write, the per-batch checkpoint save, and the per-account checkpoint and detailed-cache writes. Under reduced-permission contexts these were each stalling silently and preventing the run from progressing past the main analysis loop, even with the start-of-run paths already bounded. Storage operations now degrade to no-ops when unavailable rather than blocking the run.
[0.0.2] - 2026-04-30
Fixed
Followup to 0.0.1: extended the 3-second-timeout bounding to the run-startup paths too. Under reduced-permission contexts, the saved-data clear (setValue / dataset.drop / openDataset) and checkpoint resume (dataset.getData / getValue) were also stalling silently, preventing the run from ever reaching the main loop. With these calls bounded, runs proceed even when the underlying storage operations are unavailable.
[0.0.1] - 2026-04-30
Fixed
Bound every async finalization step (run-summary write, run-metadata fetch, first-run timestamp lookup, analytics POST) with a 3-second timeout each, plus a 60-second cleanup safety net. Under reduced-permission run contexts these calls were stalling silently and the run could reach the platform timeout instead of exiting cleanly. Completed runs now terminate within ~15 seconds of the "Run complete" log.