TikTok Profile Scraper With Bio Link & Contact Leads avatar

TikTok Profile Scraper With Bio Link & Contact Leads

Pricing

Pay per usage

Go to Apify Store
TikTok Profile Scraper With Bio Link & Contact Leads

TikTok Profile Scraper With Bio Link & Contact Leads

The TikTok Profile Scraper extracts public TikTok profile data, including usernames, bios, followers, following counts, likes, and profile URLs. Ideal for marketers, analysts, and researchers to track influencer metrics, study engagement, and build accurate audience insights efficiently.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

API Empire

API Empire

Maintained by Community

Actor stats

0

Bookmarked

19

Total users

0

Monthly active users

3 days ago

Last modified

Share

TikTok Profile Scraper With Bio Link & Contact Leads turns a list of TikTok handles into structured creator contact records: the bio link, everything found behind a Linktree/hoo.be-style aggregator, best-effort email and phone parsed from the bio and captions, plus exact follower, heart and account-age signals. Each record ships as typed, normalized JSON โ€” no HTML, no selectors, no manual cleanup. The Actor runs logged out and prints its own measured bio-link and email find-rates into the run log for every run, so coverage is observable rather than promised. Feed it a handle list and get back an outreach-ready dataset in one run.

TikTok Profile Scraper With Bio Link & Contact Leads is an Apify Actor that turns a list of TikTok creator handles into contact records built around the bio link โ€” the one field TikTok most reliably exposes for outreach โ€” plus everything the Actor can extract or infer around it. It targets TikTok's public profile page and the public item_list video endpoint; no TikTok account, login or cookie is required at any point.

  • ๐Ÿชช Build one contact record per creator: bio link, expanded aggregator destinations, best-effort email/phone, follower and account-age signals
  • ๐Ÿ”— Follow Linktree / hoo.be / Beacons / bio.link style bio links to their real destination URLs
  • ๐ŸŽฌ Pull each creator's recent videos as backward-compatible child rows, and scan captions for contacts the bio doesn't carry
  • ๐Ÿ“‡ Filter the output down to only creators that expose a contact, with an auditable uncharged reason for everyone dropped
  • ๐Ÿ“ค Export as JSON, CSV or Excel, or read the dataset straight through the Apify API

Every run produces up to three row types, distinguished by the type field: a charged creator contact record per handle, uncharged video rows behind it, and uncharged accounting rows that explain any skip or failure.

Data TypeKey FieldsJSON Field Names
Creator contact recordBio link, expanded destinations, email, phone, follower/heart counts, account agebioLink, aggregatorDestinations, primaryEmail, phones, fans, accountAgeDays
Video (creator's own recent uploads)Caption text, engagement counts, hashtags, mentions, subtitle linkstext, diggCount, playCount, hashtags, mentions, videoMeta
Accounting / audit rowWhy a handle produced no contact record, unchargedtype, errorReason, note

๐Ÿ”— Need more TikTok data?

If the lead is in the comment section rather than the bio, TikTok Comments Scraper With Commenter Profile & Contacts collects comments and enriches every unique commenter with the same bio-contact extraction used here. For raw video metrics without the contact layer, TikTok Video Scraper pulls engagement and author metadata straight from post URLs. To find which creators to target in the first place, TikTok Trending Hashtags Scraper โ€” Find New & Rising Tags flags hashtags entering the charts before they peak.

TikTok's own developer platform does not offer arbitrary bulk creator lookup โ€” it authorizes one TikTok account to share its own data with one app, which is a different problem than building a lead list from other creators' public profiles.

FeatureTikTok Developer APIsTikTok Profile Scraper With Bio Link & Contact Leads
Whose data you can readOnly the authenticated user's own profile and videos, via the Display API (developers.tiktok.com/doc/display-api-get-started, checked 2026-07-25)Any public creator handle you supply
Access modelOAuth login and explicit consent from the account owner, via Login KitNo account, no login โ€” reads the public profile page directly
Approval processApp must be reviewed and granted user.info.basic / video.list scopes before any call succeedsNone โ€” start a run from the Apify Console
Bulk creator lookupNot supported; each authorized session is scoped to one userA single run accepts a list of handles
Public-video research accessSeparate Research API, research.data.basic scope, restricted to an approved research application; queries are capped at a 30-day date range per request (developers.tiktok.com/doc/research-api-specs-query-videos, checked 2026-07-25)No application, no date-range cap on which creators you can query
Bio-link and contact extractionNot a field either API returnsBio link, aggregator expansion, best-effort email/phone are the Actor's core output

If you already operate the target account and just need its own posts inside an authorized app, the Display API is the correct, TikTok-sanctioned path. If you need public contact data across creators you don't operate โ€” the actual lead-generation use case โ€” neither TikTok API reaches it, which is the gap this Actor fills.

Why do developers and teams scrape TikTok creator profiles?

๐Ÿ“ˆ For marketers and brand teams

Influencer and partnerships teams turn a shortlist of handles into a contactable pipeline in one run: bioLink, primaryEmail and aggregatorDestinations replace the manual click-through-every-profile step, and contactScore gives an instant rank so outreach starts with the creators most likely to have a real contact channel rather than the ones alphabetically first on the list.

๐Ÿค– For AI engineers and agent builders

A lead-qualification agent can call this Actor with a batch of handles, receive typed JSON with hasContact, contactScore, commerceCategory and accountAgeDays already computed, and route each record to an outreach queue or a CRM write without a parsing step in between. The stable field names mean the same prompt template works whether the run returned 5 creators or 5,000.

๐Ÿ”ฌ For researchers and analysts

verified, commerceUser, ttSeller and accountAgeDays let a researcher segment a creator sample by account legitimacy and business status without opening a single profile manually. Because every field is read from what TikTok's own profile page already renders publicly, the dataset stays within public-data scope for academic or market-structure studies.

๐Ÿ› ๏ธ For developers building data products

statsAreExact flags whether a row's follower/heart/video counts came from TikTok's exact statsV2 string counters or a rounded fallback โ€” a distinction a lead-scoring or CRM-enrichment product needs before treating a count as precise. Feeding the dataset on a schedule keeps a downstream lookup table current without hand-rolling TikTok's hydration parsing.

How to scrape TikTok creator profiles (step by step)

  1. Open TikTok Profile Scraper With Bio Link & Contact Leads on the Apify Console and click Try for free
  2. Paste creator handles into ๐ŸŽฏ TikTok Creator Handles โ€” a bare handle, an @handle, or a full profile URL all work, one per line
  3. Set ๐ŸŽฌ Videos to pull per creator to 0 for the fastest profile-only run, or leave the default of 10 to also pull recent videos and scan their captions for contacts
  4. Turn on ๐Ÿ“‡ Keep only creators that expose a contact if you only want creators with a bio link, email or phone in the final dataset
  5. Click Start, then export the dataset as JSON, CSV or Excel, or pull it through the Apify API

What to do when TikTok changes its structure

The scraper is maintained, and the output schema is designed to stay stable across TikTok structural changes โ€” field names and types don't change on your end even when the underlying page payload does. No specific turnaround time is published; check the Actor's build history for the latest update.

โš ๏ธ What changed in TikTok scraping recently?

Two engineering findings from this Actor's own 2026-07 measurement pass changed how a logged-out TikTok scraper has to be built. First, TikTok's older SIGI_STATE / sigi-persisted-data hydration blobs are gone from the profile page โ€” the only hydration script left is __UNIVERSAL_DATA_FOR_REHYDRATION__, so any scraper still parsing the old blob name silently returns nothing. Second, TikTok now WAF-challenges desktop browser TLS fingerprints on the profile page: a challenge comes back as an ordinary HTTP 200 with a roughly 1.4 KB body containing _wafchallengeid, so checking the status code alone reports success on a page that is actually a block. This Actor fetches with a mobile TLS impersonation target (chrome99_android first) specifically because desktop fingerprints get challenged. A third, related finding: TikTok no longer server-renders a creator's post grid into the profile page at all โ€” 100% of video data now comes from the separate /api/creator/item_list/ endpoint, which hard-caps the count parameter at 10 per request regardless of what is requested. The scraper is maintained against all three; no numeric SLA is published for future changes.

โฌ‡๏ธ Input

None of the fields below are required โ€” an empty run simply logs an error and produces no rows, so at minimum fill in the creator handles field.

ParameterRequiredTypeDescriptionExample Value
creatorHandlesNoarrayOne creator per line: a bare handle (mrbeast), an @handle (@thekoreanvegan) or a full profile URL. Each creator produces exactly one charged contact-record row. No default (empty list).["thekoreanvegan", "mrbeast"]
contactsOnlyNobooleanWhen true, a creator with no bio link, email or phone is dropped from the results but still writes an uncharged accounting row naming the reason. Default false.false
expandLinkAggregatorsNobooleanFollows a Linktree/hoo.be/Beacons/bio.link-style bio link and returns the real destination URLs behind it. Default true.true
scanVideoCaptionsNobooleanRuns the email/phone/URL extractor over fetched video captions as well as the bio. Default true.true
customDomainsNoarrayOptional email-domain allowlist. Leave empty to keep every extracted address. Default [].["gmail.com", "mycompany.com"]
maxVideosPerCreatorNointegerRecent videos to fetch per creator. Minimum 0, maximum 200, default 10. Set 0 for a profile-only, contacts-only run.10
includeVideoRowsNobooleanOff keeps the dataset to contact records only; captions are still scanned either way. Video rows are never charged regardless of this setting. Default true.true
profileSortingNostringClient-side ordering of the fetched video window only โ€” TikTok's API does not sort server-side. Enum: latest (Newest first), popular (Most viewed first), oldest (Oldest of the fetched window first). Default "latest"."latest"
oldestPostDateUnifiedNostringAbsolute date (YYYY-MM-DD) or relative days-ago (e.g. 30). Filters video rows and the caption contact scan; the contact record itself is always produced regardless. No default."2026-01-01"
newestPostDateNostringAbsolute date (YYYY-MM-DD) or relative days-ago (e.g. 0 for today). No default."0"
leastDiggsNointegerMinimum heart (like) count on a video row. Minimum 1. No default.1000
mostDiggsNointegerMaximum heart (like) count on a video row. Minimum 1. No default.500000
profilesNoarrayBackward-compatible alias for creatorHandles, under the original TikTok Profile Scraper's key. When both profiles and creatorHandles are filled, profiles wins. No default.["nike"]
resultsPerPageNointegerBackward-compatible alias for maxVideosPerCreator. Minimum 0, maximum 200. Wins over maxVideosPerCreator when both are filled. No default.10
profileScrapeSectionsNoarrayAccepted for backward compatibility only. Enum items: videos, reposts. This Actor reads a creator's own videos only โ€” the value is never read by the run logic, so setting it has no effect.["videos"]
maxFollowersPerProfileNointegerAuthor-level ceiling applied to video rows: a video row is dropped when its author's follower count is at or above this value. Minimum 1. No default.5000000
maxFollowingPerProfileNointegerAuthor-level ceiling applied to video rows: a video row is dropped when its author's following count is at or above this value. Minimum 1. No default.10000
proxyConfigurationNoobjectApify Proxy settings. Prefilled to {"useApifyProxy": false} โ€” the run starts with no proxy at all. On a soft block the Actor escalates itself to datacenter, then residential, only while a creator has produced zero rows, regardless of what this field was set to.{"useApifyProxy": false}

Example input

{
"creatorHandles": ["thekoreanvegan", "mrbeast", "https://www.tiktok.com/@nike"],
"contactsOnly": false,
"expandLinkAggregators": true,
"scanVideoCaptions": true,
"customDomains": [],
"maxVideosPerCreator": 10,
"includeVideoRows": true,
"profileSorting": "latest",
"oldestPostDateUnified": "",
"newestPostDate": "",
"leastDiggs": null,
"mostDiggs": null,
"maxFollowersPerProfile": null,
"maxFollowingPerProfile": null,
"proxyConfiguration": { "useApifyProxy": false }
}

Common pitfall: filling in both a legacy key and its replacement โ€” profiles alongside creatorHandles, or resultsPerPage alongside maxVideosPerCreator โ€” is not additive. The legacy key silently wins in both cases, so a value typed into the new field appears to be ignored. Fill one of each pair, not both.

โฌ†๏ธ Output

Every row lands in the same Apify dataset with a type field marking which of the three shapes it is: profile (the charged contact record), video (an uncharged child row), or accounting (an uncharged audit row). Export as JSON, CSV or Excel, or read the dataset through the Apify API. The dataset's default table view surfaces 26 of the profile row's fields; the full row carries more, documented below from the Actor's row-building code.

Scraped creator contact record (type: "profile")

This is the only row type that is charged โ€” one row_result event per creator, whether or not a contact was found (unless contactsOnly dropped it as an uncharged skip instead).

FieldDescription
type, isChildAlways "profile" and false for this row shape
id, input, username, nickNameTikTok's internal user ID, the handle you supplied, the resolved uniqueId, and the display name
profileUrl, secUid, avatar, originalAvatarUrlCanonical profile URL, TikTok's internal secUid, and both avatar image sizes
bioLink, bioLinkHostThe raw bio-link URL exactly as TikTok stores it, and its hostname
bio, shareDescriptionThe profile's bio text (signature), and TikTok's share-card description
isLinkAggregator, aggregatorDestinations, aggregatorExpansionStatusWhether bioLink is a recognized aggregator, the real destination URLs found behind it, and how that expansion went
emails, primaryEmail, emailSourceEvery extracted email address, the first one (or null), and whether it came from "bio" or "caption"
phonesEvery extracted phone number, in its original matched format
websites, socialProfilesEvery non-asset URL found across the bio link, aggregator destinations and extracted text; and the subset matched to a known social platform
contactScore, hasContactA 0โ€“100 composite score (see formula below), and whether any contact channel was found at all
verified, commerceUser, commerceCategory, ttSeller, isOrganization, privateAccountTikTok's own account-type and business flags
accountCreatedAt, accountCreatedTimestamp, accountAgeDaysAccount creation time as ISO-8601 and as a raw Unix timestamp, and the age in days computed at scrape time
roomId, nickNameModifiedAt, uniqueIdModifiedAtLive-room ID if any, and when the display name / handle were last changed
fans, following, heart, video, digg, friendsFollower count, following count, total hearts received, video count, likes given, and friend count
statsAreExacttrue when the counts above came from TikTok's exact statsV2 string counters rather than a rounded fallback
videosAnalyzedHow many video rows were fetched and scanned behind this contact record
errorReason, scrapedAtAlways null on a successful row, and the UTC scrape timestamp

contactScore is computed as 40 (has an email) + 30 (has a bio link) + 15 (aggregator expansion found destinations) + 10 (a known social platform was matched) + 5 (has a phone number). hasContact is true when any of email, bio link, phone or aggregator destinations is present โ€” a social handle alone does not set it.

{
"type": "profile",
"isChild": false,
"id": "6829105512345678901",
"input": "fitcoachjordan",
"username": "fitcoachjordan",
"nickName": "Coach Jordan Fit",
"profileUrl": "https://www.tiktok.com/@fitcoachjordan",
"secUid": "MS4wLjABAAAA_examplesecuid",
"avatar": "https://p16-sign-va.tiktokcdn.com/avatar-medium.jpeg",
"originalAvatarUrl": "https://p16-sign-va.tiktokcdn.com/avatar-large.jpeg",
"bioLink": "https://linktr.ee/fitcoachjordan",
"bioLinkHost": "linktr.ee",
"bio": "Certified trainer | Programs & bookings below | jordan@fitcoachjordan.com",
"shareDescription": "Coach Jordan Fit on TikTok",
"isLinkAggregator": true,
"aggregatorDestinations": [
"https://fitcoachjordan.com",
"https://instagram.com/fitcoachjordan",
"https://calendly.com/fitcoachjordan"
],
"aggregatorExpansionStatus": "expanded",
"emails": ["jordan@fitcoachjordan.com"],
"primaryEmail": "jordan@fitcoachjordan.com",
"emailSource": "bio",
"phones": [],
"websites": [
"https://linktr.ee/fitcoachjordan",
"https://fitcoachjordan.com",
"https://instagram.com/fitcoachjordan",
"https://calendly.com/fitcoachjordan"
],
"socialProfiles": [
{ "platform": "instagram", "url": "https://instagram.com/fitcoachjordan" }
],
"contactScore": 95,
"hasContact": true,
"verified": false,
"commerceUser": true,
"commerceCategory": "Health & Fitness",
"ttSeller": false,
"isOrganization": false,
"privateAccount": false,
"roomId": null,
"nickNameModifiedAt": "2024-03-11T09:22:41Z",
"uniqueIdModifiedAt": "2022-08-02T14:05:19Z",
"accountCreatedAt": "2020-01-26T02:13:20Z",
"accountCreatedTimestamp": 1580004800,
"accountAgeDays": 2372,
"fans": 128400,
"following": 512,
"heart": 3120000,
"video": 340,
"digg": 8021,
"friends": 12,
"statsAreExact": true,
"videosAnalyzed": 10,
"errorReason": null,
"scrapedAt": "2026-07-25T14:02:11Z"
}

Scraped video row (type: "video")

Uncharged child rows, pushed when includeVideoRows is on. Structurally the same shape as the base TikTok Profile Scraper's video row for drop-in compatibility.

FieldDescription
type, isChild, idAlways "video", true, and TikTok's video ID
text, textLanguageCaption text and TikTok's detected language (null if not returned)
createTime, createTimeISOPost time as Unix timestamp and real UTC ISO-8601
isAd, isSponsored, isPinned, isSlideshowTikTok's own ad, sponsorship, pinned and slideshow flags
authorMetaNested object: id, name, profileUrl, nickName, verified, signature, bioLink, avatars, commerceUserInfo, privateAccount, roomId, ttSeller, following, friends, fans, heart, video, digg. followDatasetUrl is a legacy field, always null
musicMetaNested object: musicName, musicAuthor, musicOriginal, playUrl, coverMediumUrl, originalCoverMediumUrl, musicId
locationMetaNested object with address, city, cityCode, countryCode, locationName, locationId โ€” empty object {} when TikTok attaches no location
webVideoUrl, mediaUrlsCanonical video URL, and playable/downloadable media URLs
videoMetaNested object: height, width, duration, coverUrl, originalCoverUrl, definition, format, subtitleLinks (array of caption-track objects)
diggCount, shareCount, playCount, collectCount, commentCount, repostCountEngagement counters, exact statsV2 when available
hashtags, mentions, detailedMentionsHashtag names, @-mentions, and a byte-identical duplicate of mentions kept for base compatibility
effectStickersEffect/sticker metadata attached to the post
commentsDatasetUrlLegacy field, always null โ€” no linked comments dataset is produced by this Actor
input, fromProfileSection, scrapedAtThe handle you supplied, always "videos", and the UTC scrape timestamp
{
"type": "video",
"isChild": true,
"id": "7398201847561234567",
"text": "Booking now open for the fall program! Link in bio #fitness #onlinecoaching",
"textLanguage": "en",
"createTime": 1753372800,
"createTimeISO": "2025-07-24T18:20:00Z",
"isAd": false,
"authorMeta": {
"id": "6829105512345678901",
"name": "fitcoachjordan",
"profileUrl": "https://www.tiktok.com/@fitcoachjordan",
"nickName": "Coach Jordan Fit",
"verified": false,
"signature": "Certified trainer | Programs & bookings below | jordan@fitcoachjordan.com",
"bioLink": "https://linktr.ee/fitcoachjordan",
"originalAvatarUrl": "https://p16-sign-va.tiktokcdn.com/avatar-large.jpeg",
"avatar": "https://p16-sign-va.tiktokcdn.com/avatar-medium.jpeg",
"commerceUserInfo": { "commerceUser": true, "category": "Health & Fitness" },
"privateAccount": false,
"roomId": "",
"ttSeller": false,
"followDatasetUrl": null,
"following": 512,
"friends": 12,
"fans": 128400,
"heart": 3120000,
"video": 340,
"digg": 8021
},
"musicMeta": {
"musicName": "original sound",
"musicAuthor": "fitcoachjordan",
"musicOriginal": true,
"playUrl": "https://sf16-ies-music.tiktokcdn.com/track.mp3",
"coverMediumUrl": "https://p16-sign-va.tiktokcdn.com/music-cover.jpeg",
"originalCoverMediumUrl": "https://p16-sign-va.tiktokcdn.com/music-cover.jpeg",
"musicId": "7398201800001234567"
},
"locationMeta": {},
"webVideoUrl": "https://www.tiktok.com/@fitcoachjordan/video/7398201847561234567",
"mediaUrls": ["https://v16.tiktokcdn.com/video-play.mp4"],
"videoMeta": {
"height": 1024,
"width": 576,
"duration": 38,
"coverUrl": "https://p16-sign-va.tiktokcdn.com/video-cover.jpeg",
"originalCoverUrl": "https://p16-sign-va.tiktokcdn.com/video-cover-orig.jpeg",
"definition": "720p",
"format": "mp4",
"subtitleLinks": []
},
"diggCount": 15234,
"shareCount": 342,
"playCount": 210500,
"collectCount": 980,
"commentCount": 512,
"repostCount": 12,
"mentions": [],
"detailedMentions": [],
"isPinned": false,
"commentsDatasetUrl": null,
"hashtags": [{ "name": "fitness" }, { "name": "onlinecoaching" }],
"effectStickers": [],
"isSlideshow": false,
"isSponsored": false,
"input": "fitcoachjordan",
"fromProfileSection": "videos",
"scrapedAt": "2026-07-25T14:02:14Z"
}

Accounting / audit row (type: "accounting")

Uncharged. Written whenever a handle produces no contact record โ€” a WAF block, an unparseable page, a missing/private/removed profile, or a contactsOnly skip.

FieldDescription
type, isChild, idAlways "accounting", false, and null
input, username, profileUrlThe handle you supplied, and the profile URL it resolved to
bioLink, bio, emails, primaryEmail, phones, websites, socialProfiles, aggregatorDestinations, hasContactAll reset to their empty defaults โ€” null, [] or false
errorReasonOne of waf_blocked, unparseable_page, profile_not_found, profile_private, profile_unavailable, profile_data_missing, filtered_no_contact, unexpected_error, or a dynamic tiktok_status_<code> for a status code not in that list
noteHuman-readable detail for the reason, or null
scrapedAtUTC timestamp

A contactsOnly skip row additionally carries fans (the follower count TikTok returned before the row was dropped), so a filtered creator's reach is still visible in the audit trail.

{
"type": "accounting",
"isChild": false,
"id": null,
"input": "randomcreator22",
"username": "randomcreator22",
"profileUrl": "https://www.tiktok.com/@randomcreator22",
"bioLink": null,
"bio": null,
"emails": [],
"primaryEmail": null,
"phones": [],
"websites": [],
"socialProfiles": [],
"aggregatorDestinations": [],
"hasContact": false,
"errorReason": "filtered_no_contact",
"note": "contactsOnly=true and this profile exposed no bio link, email or phone",
"fans": 842,
"scrapedAt": "2026-07-25T14:02:09Z"
}

Charged rows only carry row_result on type: "profile". To read only the billed rows from an exported dataset, filter on type == "profile"; everything with type of "video" or "accounting" was pushed for free.

FeatureTikTok Profile Scraper With Bio Link & Contact LeadsGeneric alternative
Output formatTyped JSON, stable schema, exported as JSON/CSV/ExcelVaries; several competitors ship a similar shape but with fewer documented fields
Aggregator expansionFollows Linktree/hoo.be/Beacons/bio.link and returns the real destination URLs, honestly reporting no_ssr_links when a JS-only aggregator exposes nothingNot documented on the competing listings reviewed
Charging on failureOnly a successfully parsed contact record is charged; WAF blocks, unparseable pages and contactsOnly skips write an uncharged accounting rowpeakydev/leads-scraper-ppe's listing states a 100-lead minimum charge per run regardless of results returned (checked on the Apify Store 2026-07-25 โ€” not verified against its source)
Entity coverageContact record, video rows and audit rows in one rungocreative.ai/tiktok-profile-scraper's listing documents profile rows only, with no video-level data (checked on the Apify Store 2026-07-25)
Login requirementNone โ€” reads the public profile page over a mobile TLS fingerprintgocreative.ai/tiktok-profile-scraper's listing also advertises no login required (checked on the Apify Store 2026-07-25)

If you're building an AI agent or RAG pipeline, the output-format row is the decision-maker โ€” parsing HTML inside an agent loop is a reliability failure mode, not a feature.

There is no hard cap on how many creator handles you can submit in creatorHandles โ€” the Actor processes the list one handle at a time. Per creator, maxVideosPerCreator caps video rows at 200, matching the ceiling the Actor enforces in code; reaching it takes roughly 20 sequential paginated requests per creator because TikTok's item_list endpoint hard-caps its own count parameter at 10 items per request regardless of what is asked for. Set maxVideosPerCreator to 0 for the fastest possible run when you only need the contact record. No benchmark run time is published โ€” actual duration scales with how many creators you submit and how many videos you request per creator.

TikTok Profile Scraper With Bio Link & Contact Leads works with any language or tool that can send an HTTP request, since it runs as a standard Apify Actor.

REST API integration

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/tiktok-profile-scraper-with-bio-link-contact-leads").call(run_input={
"creatorHandles": ["thekoreanvegan", "mrbeast"],
"contactsOnly": False,
"maxVideosPerCreator": 10,
})
for lead in client.dataset(run["defaultDatasetId"]).iterate_items():
if lead.get("type") == "profile":
print(lead["username"], lead["bioLink"], lead["primaryEmail"])

Works in Python, Node.js, Go, Ruby, cURL.

Automation platforms (n8n, Make, LangChain)

In n8n, use the Apify node โ€” select this Actor, pass the same JSON input shown above, and route dataset items straight into a CRM or Google Sheets node on a schedule. In Make, the Apify module runs the Actor and hands its dataset output to any downstream module, so a weekly creator-list refresh needs no code. In a LangChain or similar agent framework, wrap the apify_client call above as a tool โ€” the Actor returns typed JSON, so the agent can read hasContact and contactScore directly without a parsing step.

Yes โ€” TikTok Profile Scraper With Bio Link & Contact Leads reads only what TikTok already renders on a creator's public profile page to any anonymous, logged-out visitor. No login-gated or private content is accessed.

Because the output includes personal data โ€” a bio, a follower count, and any email or phone the Actor extracts โ€” data-protection regimes like GDPR and CCPA apply to how you store and use it, not to whether the initial collection is technically possible. You need a lawful basis for processing (typically legitimate interest for B2B outreach) and must honor deletion and access requests from the individuals concerned. Extracted contact details should also be checked against outreach and anti-spam rules (e.g. CAN-SPAM) in your jurisdiction before use.

Consult legal counsel for commercial use cases involving bulk personal data.

โ“ Frequently asked questions

Yes. The Actor sends anonymous requests to TikTok's public profile page and video endpoint over a mobile-impersonated TLS session โ€” no TikTok login, cookie or session token is ever used, and the input schema has no field for one. The only credential you need is your Apify token.

How often is the scraped data updated?

Every run performs a live fetch against TikTok at the moment it runs โ€” nothing is cached or served from a stored snapshot. Re-run the Actor to get current follower counts, bio text and video data.

What happens if a creator's profile is private, removed or doesn't exist?

The Actor pushes an uncharged accounting row instead of a contact record, with errorReason set to profile_private, profile_not_found, profile_unavailable, or profile_data_missing depending on what TikTok returned. No fabricated or partial contact record is produced for a handle that failed.

Can I scrape private TikTok accounts?

No. Only publicly accessible profile data is returned. A private account resolves to an uncharged profile_private accounting row rather than any profile data.

Is there a minimum charge or free trial?

Pricing and any free-usage allowance are set on the Actor's Apify Store listing under the Pricing tab, not in this README โ€” check the listing for the current terms before running at volume.

Yes. It's callable as a standard HTTP endpoint through the Apify API, so any agent framework โ€” LangChain, CrewAI, a custom tool definition โ€” can invoke it and receive typed JSON directly, with no parsing step before passing a record into an LLM context window.

It fetches with mobile TLS impersonation (chrome99_android first, falling back through three other mobile/desktop targets) because TikTok WAF-challenges desktop fingerprints. A challenge response โ€” HTTP 200 with a short body containing _wafchallengeid โ€” is detected by body content and size, not by status code, and triggers a retry with the next fingerprint. If fingerprint rotation alone doesn't clear it, the Actor escalates itself from no proxy to a datacenter proxy, then a residential proxy, but only while a given creator has produced zero rows โ€” so a retry can never duplicate or double-charge a row.

Checked on the Apify Store on 2026-07-25, gocreative.ai/tiktok-profile-scraper's listing advertises email, Instagram-handle and Linktree extraction from the bio with no login required, similar in scope to this Actor's contact layer but documented as profile-only, with no video rows. peakydev/leads-scraper-ppe and peakydev/leads-scraper target a broader, cross-platform lead database (their listings describe "700M verified leads... similar to Apollo") rather than TikTok specifically, and their listings state a 100-lead minimum charge per run. This Actor's difference is the aggregator-expansion layer โ€” following a Linktree-style bio link to its real destination URLs rather than stopping at the aggregator link itself โ€” combined with an uncharged, auditable accounting row for every creator that produces no contact record.

Yes. Every response is typed, normalized JSON with stable field names across runs. No HTML, no selectors, no parsing. Pass a row directly into an LLM context window, index it into a vector store, or route it through an agent tool.

Yes, in the sense that no proxy is required to start โ€” the default input runs with no proxy at all. If TikTok soft-blocks a request, the Actor manages the proxy ladder itself (datacenter, then residential) without any action from you. If you want a specific behavior from the start, set proxyConfiguration to your preferred Apify Proxy group.

What happens when TikTok changes its structure or blocks the scraper?

The scraper is maintained, and the output schema is built to stay stable on your end โ€” field names and types don't change even when TikTok's underlying page structure does. No numeric turnaround time is promised for a fix after a breaking TikTok change.

๐Ÿ’ฌ Your feedback

Found a bug, or a field in TikTok's profile payload that isn't in the output yet? Open an issue on the Actor's Issues tab on Apify โ€” a report backed by the affected handle and a run log is the fastest to act on.