Automated data broker opt-out runner for macOS, Linux, and Windows. Removes your personal information from 500+ people-search sites and data broker databases on a monthly schedule - with CAPTCHA solving, persistent state tracking (so completed opt-outs aren't resubmitted every run), and an iMessage notification when done. Privacy & data flow ->
Each month, the script:
Searches each data broker site for your name + stateFinds your specific listing(for sites that need a profile URL)** Fills and submitsthe opt-out form automatically Solves CAPTCHAsviaCapSolver(AI-powered, ~$0.001/solve) Skipsbrokers you were already removed from recently (90-day re-check window) Sends you an iMessagewith the results summary Opens**any sites that require manual action in your browser
- Node.js 18+
- macOS, Linux, or Windows (scheduling adapts automatically) Playwrightbrowsers installed
npx playwright install chromium
git clone https://github.com/stephenlthorn/auto-identity-remove.git
cd auto-identity-remove
npm install
node setup.js
./run.sh
node setup.js
guides you through:
| Step | What it does |
|---|---|
| Personal info | |
| Name, city, state, ZIP, email, phone | |
| Aliases | |
| Past names or variations (e.g. "Steve Doe") | |
| CapSolver key | |
| For CAPTCHA-protected opt-out forms | |
| One-time accounts | |
Creates accounts on sites that require login (stored in config.json , gitignored) |
|
| iMessage | |
| Phone number to text the results summary to | |
| Monthly schedule | |
| Registers a monthly job to run on the 1st at 9am (launchd / systemd / crontab / schtasks - detected automatically) |
Your personal info never leaves your machine. config.json
and state.json
are both gitignored.
Some opt-out forms have reCAPTCHA. Without CapSolver, those sites go to your manual list instead of being handled automatically.
- Sign up at capsolver.com- free, pay-as-you-go - Add $1-2 of credits (enough for months of use at ~$0.001/solve)
- Paste your API key when
setup.js
asks, or add it toconfig.json
:
"capsolver": {
"apiKey": "CAP-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
CapSolver is optional.Without it, CAPTCHA-protected sites are flagged as manual and opened in your browser for completion. Pass--no-capsolver
to skip them entirely rather than opening the browser.
The included Dockerfile
uses the official Playwright image, so Chromium and all system dependencies are pre-installed. No Mac required.
docker build -t auto-identity-remove .
docker run --rm \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/state.json:/app/state.json \
auto-identity-remove node watcher.js --dry-run
docker run --rm \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/state.json:/app/state.json \
auto-identity-remove
Persistent state: mount state.json
so completed opt-outs are remembered
between container runs. If the file does not exist yet, create an empty one
first: echo '{}' > state.json
.
When running headless or in Docker you won't have iMessage or a desktop - use
a webhook instead. Set notify.webhook
in config.json
to any ntfy.sh, Slack incoming-webhook, or Discord webhook URL:
"notify": {
"textTo": "",
"webhook": "https://ntfy.sh/my-private-channel"
}
The tool POSTs {"text": "<summary>"}
after every run. Works on macOS, Linux, and Windows - the webhook fires in addition to (not instead of) any platform notification that is available.
auto-identity-remove/
βββ setup.js β Run once: interactive setup + scheduling
βββ watcher.js β Main runner
βββ brokers.js β Broker list with opt-out strategies
βββ run.sh β Manual trigger
βββ config.example.json β Template (copy β config.json)
βββ package.json
βββ .gitignore
β
βββ config.json β YOUR personal info (gitignored, created by setup.js)
βββ state.json β Opt-out history / skip logic (gitignored)
βββ logs/ β Per-run JSON logs (gitignored)
state.json
tracks when each broker was last successfully opted out. The default re-check window is 90 days - brokers typically re-add your data within that window, so the script re-submits when it's time.
{
"optOuts": {
"Spokeo": {
"lastSuccess": "2026-05-01T09:00:00.000Z",
"totalRuns": 3,
"detail": ""
}
}
}
On each run you'll see:
β
Submitted (form accepted)
-
opt-out form was submitted this run
π§ Awaiting email confirm -
broker replied "check your email to confirm"; click the link in your inbox. Auto-retried after 14 days if still pending.
β Skipped (fresh) -
removed recently, re-check not due yet
π Not listed -
your name wasn't found on that site
π Manual needed -
opened in your browser for you to handle
β Error -
network/timeout issue, will retry next run
π Dead (stale URL) -
broker URL is gone (DNS/404); not counted as an error
Submitted β confirmed deleted.Usenode watcher.js --verify
for spot-check verification. See[STATUS.md]for a per-broker confidence table.
This tool covers 500+ data brokers in two tiers:
| Tier | Count | Confidence |
|---|---|---|
| Explicit brokers ( | ||
| 42 | Hand-mapped with specific selectors. verified entries have been tested live; untested ones may have drifted since they were added. |
|
| Generic runner | ||
| ~490 | Best-effort heuristic - tries 4 strategies (Do Not Sell click, OneTrust/TrustArc, generic form, DSAR link). Many succeed; some fail silently. |
The β
Submitted
count means the form was accepted by the broker. It does not prove deletion. To check:
-
Run
node watcher.js --verify -
re-searches each broker where a successful opt-out was recorded and reports whether your name still appears. - Look at the
π§ Awaiting email confirm
section after each run - these are half-done until you click the link.
If you want to know exactly which brokers are hand-verified vs heuristic, see STATUS.md.
| Site | Method |
|---|---|
| Spokeo | Search β find listing β opt-out form |
| WhitePages | Search β find listing β suppression form |
| FastPeopleSearch | Search β opt-out form |
| TruePeopleSearch | Direct opt-out form |
| BeenVerified | Opt-out search form |
| Radaris | Search β privacy form |
| Intelius | Direct opt-out form |
| PeopleFinders | Direct opt-out form |
| PeopleSmart | Direct opt-out form |
| MyLife | Search β opt-out |
| Nuwber | Search β removal form |
| FamilyTreeNow | Direct opt-out form |
| CheckPeople | Direct opt-out form |
| ThatsThem | Direct opt-out form |
| USPhonebook | Direct opt-out form |
| PublicDataUSA | Direct opt-out form |
| SmartBackgroundChecks | Direct opt-out form |
| SearchPeopleFree | Direct opt-out form |
| PeopleSearchNow | Direct opt-out form |
| InfoTracer | Direct opt-out form |
| SocialCatfish | Direct opt-out form |
| NationalPublicData | Direct opt-out form |
| ClustrMaps | Direct opt-out form |
| PrivateRecords | Direct opt-out form |
| Acxiom | |
| Direct form (feeds dozens of downstream brokers) | |
| LexisNexis | |
| Direct form (legal/financial data) | |
| ZoomInfo | |
| Direct form (B2B professional data) | |
| Clearbit | |
| Direct form (B2B enrichment data) | |
| Pipl | Email opt-out via Mail.app |
generic-runner.js
handles the remaining ~470 brokers from two public datasets:
| Dataset | Source | Count |
|---|---|---|
BADBOOLFor each site it tries four strategies in order:
- Click a "Do Not Sell My Personal Information" button
- Opt out via OneTrust / TrustArc / Osano privacy manager
- Fill any generic opt-out form (email, name, state) and submit
- Find and record a DSAR / data request link for manual follow-up
Sites requiring manual action are opened in your browser automatically.
| Site | Why manual |
|---|---|
| Google - Results About You | Requires Google account interaction |
| Google - Outdated Content | Case-by-case URL submission |
Edit brokers.js
and add an entry:
{
name: 'NewBrokerSite',
method: 'direct-form', // or 'search-form', 'email', 'manual'
optOutUrl: 'https://example.com/opt-out',
formFields: {
'input[name*="first" i]': F, // F, L, N, E, ST, Z are from config
'input[name*="last" i]': L,
'input[type="email"]': E,
},
submitSelector: 'button[type="submit"]',
captchaLikely: false,
priority: 2,
}
PRs welcome - especially for brokers with verified working selectors.
./run.sh
Dry-run mode - navigates to each site and fills forms but does NOT submit anything. Good for verifying what the script will do before your first real run:
node watcher.js --dry-run
Or to run in the background and log output:
./run.sh >> logs/manual-run.log 2>&1 &
Run a read-only spot-check to see whether previous opt-outs are still in effect:
node watcher.js --verify
This opens a browser, searches each broker where you have a recorded successful opt-out, and reports what it finds. No forms are submitted, nothing is written to state.json
.
Output is grouped into three sections:
| Section | Meaning |
|---|---|
VERIFIED CLEAR |
|
| Your name was not found in the broker's search today | |
STILL LISTED |
|
| A listing was found - the opt-out may have failed, or your data was re-added | |
UNVERIFIABLE |
|
| The broker uses a direct-form, email, or manual method - no automated search signal exists to check |
A dated JSON report is saved to logs/verify-YYYY-MM-DD.json
.
Important caveats:
- Only
search-form
brokers (those with asearchUrl
andlistingPattern
) can be checked automatically. Direct-form and email opt-outs are alwaysunverifiable
. - "Verified clear" means your name was not found in one search today. It is
not a legal guarantee of deletion. Brokers routinely re-ingest data from upstream sources. - "Still listed" can mean the opt-out failed
or the broker re-added your data since the last successful opt-out was recorded. Either way, re-runningnode watcher.js
will attempt removal again. - If the broker's search page is down or slow, the result is classified as
unverifiable
(a timeout is not counted as "still listed").
WARNING: This feature may violate broker Terms of Service.Submitting fabricated opt-out requests to data broker sites is ethically questionable and could expose you to legal risk. Use at your own discretion. This feature isoff by defaultand is provided only as a research/experimental tool.
The --pollute N
flag submits N
randomly-generated fake person records to data brokers that are explicitly tagged acceptsBogus: true
in brokers.js
. The goal (inspired by a suggestion on HN) is to flood broker databases with junk records, degrading the accuracy of their search results.
node watcher.js --pollute 10
Each fake record uses:
- A random name from a small fixture list (not real people)
- A US city/state/zip from a fixture of 50+ valid combos (not your address)
- A 10-digit phone with an area code valid for the fake state
- A randomised
firstname.lastname+XXXXXX@gmail.com
Only brokers tagged acceptsBogus: true
in brokers.js
will receive noise submissions. Currently tagged: ThatsThem, SearchPeopleFree, PeopleSearchNow, InfoTracer, SocialCatfish. These are direct-form brokers with no SSN/DOB gate.
Regular opt-outs run first - noise submissions happen after the normal run. The --pollute
flag has no effect on your real opt-out submissions.
The Markup dataset is years old; many of the ~489 generic opt-out URLs now 404 or fail DNS lookup. These are classified as π Dead (stale URL)
in run output and do not count as errors.
After several runs have accumulated in logs/
, trim permanently-dead hostnames from future runs so they are skipped without any network request:
node scripts/prune-dead.js
The script:
- Reads every
logs/run-*.json
file - Finds hostnames whose status was
dead
inevery run they appeared in - Merges them into
data/dead-urls.json
(deduped, sorted) - Prints a summary of how many new hosts were added
The script is idempotent - running it twice produces no change. You can add it as a post-run step or run it manually whenever you want to prune the dead list.
data/dead-urls.json
is committed to the repo so the dead list is shared with all clones.
| Platform | Command |
|---|---|
| macOS (launchd) | |
launchctl unload ~/Library/LaunchAgents/com.auto-identity-remove.plist then rm ~/Library/LaunchAgents/com.auto-identity-remove.plist |
|
| Linux (systemd) | |
systemctl --user disable --now auto-identity-remove.timer then rm ~/.config/systemd/user/auto-identity-remove.{service,timer} |
|
| Linux (crontab fallback) | |
Run crontab -e and delete the auto-identity-remove line |
|
| Windows (schtasks) | |
schtasks /Delete /TN auto-identity-remove /F |
This tool supports non-US users with a few important caveats.
setup.js
will prompt forCountry(2-letter ISO code, e.g.CA
,GB
,AU
) and then replace the US-centric "State" / "ZIP code" prompts withProvince/Region andPostal code prompts that accept any format (K1A 0A6
,SW1A 1AA
,2000
, etc.) with no coercion.- Phone numbers for non-US users are stored verbatim - no
(xxx) xxx-xxxx
reformatting is applied. lib/forms.js
automatically tries province/postal/postcode HTML field variants (e.g.input[name*="province"]
,input[name*="postcode"]
) when filling forms for non-US users, with no change needed in broker definitions.- A country
<select>
on opt-out forms is targeted and filled with your 2-letter country code when present. - Global brokers (ZoomInfo, Clearbit, Acxiom, Radaris, etc.) are attempted for all users.
The following brokers are flagged usOnly: true
and are silently skipped when your configured country is not US
. These sites index US public records, voter data, or phone directories - a non-US person definitionally has no record to remove there:
| Broker | Reason |
|---|---|
| Spokeo | US people-search (state-keyed search) |
| WhitePages | US white-pages directory |
| FastPeopleSearch | US people-search |
| TruePeopleSearch | US people-search |
| BeenVerified | US background-check (requires US state) |
| USPhonebook | US phone directory |
| PublicDataUSA | US public records |
All other brokers in the list are attempted regardless of country.
US people-search sites (Spokeo
, WhitePages
, etc.) hold records sourced from US public records - if you have never lived in the US, your data is very unlikely to appear on these sites. The script skips them for you automatically.
A fair concern raised by some users: aren't you just confirming your data to the brokers by filling out their forms?
A few things worth knowing:
These brokers already have your info. You're not revealing anything new - you're using the legally-required removal mechanism they're obligated to provide.CCPA (California) and similar state laws require brokers to honor opt-out requests. Submitting the form creates a legal obligation to remove you. Doing nothing does not.The script uses info you're already listed under- your name as it appears publicly, your state, your email. It doesn't add new data points.** The alternative is worse.**Every month that passes, more brokers scrape and resell your data. Opt-outs are imperfect, but they work more often than not.
That said: if you're in a situation where even confirming your email address to a broker is a risk, this tool is not the right approach. Consider a paid service that uses a proxy email.
California is launching an official Delete Me opt-out registry on August 1, 2025. Once registered, data brokers are legally required to delete your info automatically - no individual form submissions needed for participating brokers.
Register at: ** optoutregistry.oag.ca.gov** (live August 1)
Recommended: Register with the CA Delete Registry first, then run this script for the brokers that aren't covered.
Paid services like Incogni ($96/yr) or Optery ($39/yr) are excellent and cover more brokers with professionally maintained opt-out flows. This tool is for people who want full control, transparency, and no recurring subscription - or who want to handle the gaps those services miss (Acxiom, LexisNexis, ZoomInfo, Clearbit).
Using both is the strongest approach: a paid service for the bulk of brokers + this script for the gaps.
MIT