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.
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:
- Dump the accessibility tree of whatever is on screen.
- Skip it if it’s a LIVE broadcast or a confirmed ad.
- Swipe right-to-left to open the creator’s profile.
- Wait for the profile stats to actually populate, refreshing if they don’t.
- Read every piece of text on the profile and run the qualification chain.
- Qualified → append a CSV row, return to the feed, like the video.
- 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.
| # | Check | Rejection reason |
|---|---|---|
| 1 | A handle is visible | profile handle not found |
| 2 | Not on your manual exclusion list | blocked handle |
| 3 | Not already in the CSV | duplicate handle |
| 4 | Bio matches your niche terms or emojis | not in niche |
| 5 | Location signal present (optional) | no visible location signal |
| 6 | Follower count parsed | follower count not found |
| 7 | Followers within your configured range | followers outside target range |
| 8 | A real email exists — in the bio, or behind TikTok’s Email button | no actual email address |
| 9 | That email isn’t already in the CSV | duplicate email |
| 10 | That email isn’t a management or booking inbox | agency/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.
| Column | Contents |
|---|---|
timestamp | ISO-8601, when the row was written |
handle | @creator |
followers | As displayed — 12.3K |
email | Lowercased |
location_signal | Whether a location hint was found |
profile_text | Full flattened profile text, for spot-checking why something qualified |
What you need to run it
- Python 3.10+ and
adbon 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_textcolumn. - 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.
Want something like this pointed at your business instead? See how I work · Back to all tools
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.
Privacy Policy · Terms of Service · FAQ · Contact · © 2026 AI with Damon
