AI/D — Tools

TikTok
Lead Collector

No API. No headless browser. A Python script that drives a real Android phone over ADB — scrolling the For You feed, opening creator profiles, reading the live accessibility tree, and writing qualified leads to a CSV.

Free · Open source (MIT) · Python 3.10+ · Runs unattended on a spare phone

8h 14mUnattended runtime
1,472Videos scanned
127Contactable leads
$0Cost per lead

One real session. One phone on a desk. No API keys, no proxies, no scraping service, no per-lead fee.

Why a phone instead of an API

TikTok has no public API for this, and the web endpoints that used to work are heavily defended. So if you want to scrape TikTok without an API, the phone in your drawer is the way in: a fully authenticated, fully rendered TikTok client that nobody is trying to block.

The tradeoff is that you are now automating a UI instead of parsing JSON, and UIs are hostile. Most of this codebase isn’t “find the email” — it’s dealing with a feed that doesn’t advance, profiles that render empty, ads that eat your gestures, and an ADB connection that drops somewhere around hour three.

How it works

The loop handles one item at a time:

  1. Dump the accessibility tree of whatever is on screen.
  2. Skip it if it’s a LIVE broadcast or a confirmed ad.
  3. Swipe right-to-left to open the creator’s profile.
  4. Wait for the profile stats to actually populate, refreshing if they don’t.
  5. Read every piece of text on the profile and run the qualification chain.
  6. Qualified → append a CSV row, return to the feed, like the video.
  7. Not qualified → return to the feed and move on.

Everything is regex over the accessibility tree. There are no screenshots and no model calls, which is why it can run for eight hours on a phone and cost nothing.

The 10-stage qualification chain

Checks run in order and stop at the first failure — so the rejection tally at the end of a run tells you exactly where you’re losing people, not just that you lost them.

#CheckRejection reason
1A handle is visibleprofile handle not found
2Not on your manual exclusion listblocked handle
3Not already in the CSVduplicate handle
4Bio matches your niche terms or emojisnot in niche
5Location signal present (optional)no visible location signal
6Follower count parsedfollower count not found
7Followers within your configured rangefollowers outside target range
8A real email exists — in the bio, or behind TikTok’s Email buttonno actual email address
9That email isn’t already in the CSVduplicate email
10That email isn’t a management or booking inboxagency/management inbox

Why the ordering earns its keep. In the session above, 239 profiles failed at stage 8 — no reachable email. Because email is checked last, every one of those had already passed the niche and follower filters. They were otherwise perfect leads with no address on them. That’s a number you can act on: it says go find the emails, not go fix the targeting.

What TikTok does about it, and what the script does back

Profiles that render empty

TikTok frequently serves a profile with 0 Followers / 0 Following before the real numbers arrive. Judging that page would silently reject good creators, so zeroed or missing stats are treated as “not loaded” — the script pulls to refresh and re-checks.

Gestures that get eaten

Interactive ads swallow the horizontal swipe while leaving you on the feed. Every navigation is verified by comparing a signature of the screen before and after, rather than assuming the gesture worked.

A feed that won’t advance

The swipe escalates through three progressively stronger gestures; if the feed signature still hasn’t changed, it restarts the app and forces the next item.

ADB dropping around hour three

Connection errors are detected by message, and the script disconnects, reconnects, and rebuilds the uiautomator2 session without losing its place.

What you actually get

A deduplicated CSV that behaves like a very small CRM. Every qualified lead is written the moment it’s found, so an interrupted run loses nothing, and dedupe runs across sessions on both handle and email — the existing CSV is loaded at startup.

ColumnContents
timestampISO-8601, when the row was written
handle@creator
followersAs displayed — 12.3K
emailLowercased
location_signalWhether a location hint was found
profile_textFull flattened profile text, for spot-checking why something qualified

What you need to run it

  • Python 3.10+ and adb on your PATH
  • An Android phone with USB debugging on, TikTok installed and logged in
  • A config file — your niche terms, follower range, output path, device serial

Plug in once, run adb tcpip 5555 and adb connect <phone-ip>:5555, and it runs over wifi from then on. A dedicated phone left on a shelf is the intended setup. Always do a short --max-videos 10 run first to confirm your gestures land on your device.

Honest limitations

  • It doesn’t open the link in the bio. It reads the bio text and TikTok’s Email button, but not the Linktree behind the profile link — where a large share of those 239 “no email” rejections are hiding. Closing this is the single biggest available yield increase.
  • Niche matching is substring-based. Convenient, but short terms match inside longer words. Use distinctive terms and spot-check the profile_text column.
  • Roughly a third of profile-open attempts fail. Only the swipe is used and it isn’t retried; an avatar-tap fallback would recover most of them.
  • The agency filter is an inbox heuristic, not real representation detection.
  • Timing is fixed. All delays are constants, which makes the action cadence very regular over a long run.

Read this before you run it

Automating the TikTok app is against TikTok’s Terms of Service. You are risking the account you run it on, and possibly the device. Use an account you can afford to lose.

The addresses this collects are personal data, even when publicly listed. If you email them you are subject to CAN-SPAM in the US, GDPR in the EU, and their local equivalents — honest headers, a real physical address, a working unsubscribe, and opt-outs honoured promptly. Under GDPR, “it was on their public profile” is not by itself a lawful basis for marketing email.

The blocked_handles list exists so that when someone asks not to be contacted, you can make that stick. Use it. And don’t commit your CSV — leaking a file of other people’s addresses is a real harm to real people.

Take the code

MIT licensed. Clone it, point it at your niche, and change whatever you like.

View on GitHub → Read why I built it →

Want something like this pointed at your business instead? See how I work · Back to all tools

Damon Scaife

Built by Damon Scaife

I build automation systems for small businesses, and I use them on my own first. This one came out of needing creator leads and refusing to pay per-lead for them. If you build something on top of it, tell me what you did.

Get new tools first

New tools and build teardowns ship regularly. One short email when something new drops — no spam, unsubscribe anytime.