~ / guides / How to Scrape Emails From Twitter (X) (2026)

How to Scrape Emails From Twitter (X) (2026)

KM
Kit Mason
X data engineer · about the author
the short version
  • You cannot pull emails from X's API. I checked in July 2026: the X API returns an email only for the logged-in account, never for another handle. Every email you collect comes from the public profile.
  • Emails on X live in four user-controlled spots: the bio, the website field, a pinned post, and the contact page of any linked site. A scraper reads those and pulls the address out.
  • The reliable route is fetch-then-parse: get the profile as JSON, then run a regex over the bio and website. I show the Python, including a de-obfuscator for the name [at] domain [dot] com trick.
  • For volume I send each profile to the ChocoData API so the logged-out wall, tokens, and proxies are handled server-side, then I verify every address and check GDPR and CAN-SPAM before sending.

I needed a few hundred verified business emails from X for an outreach test, and the first thing I confirmed is the detail most guides skip: X never hands you an email through its API. I checked again in July 2026, and the only email the X API returns is the one belonging to the account you authenticated with. Every other email you collect from X, you collect by reading the public profile the way a person would.

So learning how to scrape emails from Twitter is really two jobs stitched together: fetch the public profile, then pull the address out of the text the user chose to publish. This guide is the procedure I actually ran - where the emails hide, the Python that extracts them, the managed route that fetches profiles without hitting the login wall, and the legal lines that decide whether you should run it at all.

Can you scrape emails from Twitter (X)?

You can scrape emails from Twitter (X), but only the emails users publish themselves on a public profile, because X’s API never exposes another account’s email address. The API returns a confirmed email only for the authenticated user, the account whose token you hold, and there is no lookup that turns a handle or user ID into someone else’s email. That restriction is deliberate, tied to privacy and data-protection rules, and it does not lift at a higher tier.

The pricing makes the point sharper. X moved its v2 API to pay-per-use, and even at Enterprise level the profile object it returns has no email field at all, per the official X API documentation. Paying more buys more reads, not an email column.

That is why an email scraper is a different tool from the API. It loads the rendered public profile, then parses the email out of the fields a user filled in by hand. The next thing to settle is exactly which fields those are.

Where do emails live on an X (Twitter) profile?

Emails live on an X profile in a handful of user-controlled places, all of them public and all of them optional. A person who wants to be contacted puts an address where a reader can see it, and those are the only spots a scraper can legitimately read.

Most accounts publish no email anywhere, so yield depends heavily on who is on your list. Engaged accounts and businesses surface a public address far more often than a random personal account does. With the target fields known, here is the Python that pulls them.

How do you scrape emails from Twitter with Python?

You scrape emails from Twitter with Python in three steps: build a list of target profiles, fetch each public profile, then extract the email from the bio and website text with a regular expression. The regex is the easy part, and it is the same no matter where the profile text came from. Getting the profile reliably is the hard part, which the next section handles at volume.

Step 1: Build your list of target profiles

Start the list from a keyword search or an engaged audience, because list quality decides your email yield more than any other choice. A search for a role like “ecommerce founder” returns accounts that tend to be real businesses with a public contact address, where a celebrity’s follower dump does not.

import requests

API = "https://chocodata.com/api/v1"
KEY = "YOUR_CHOCO_API_KEY"

# Keyword-matched accounts carry public emails far more often
# than a random follower list, so start from a search.
r = requests.get(f"{API}/twitter/search",
                 params={"q": "ecommerce founder", "api_key": KEY}, timeout=30)
tweets = r.json()["data"]["tweets"]
handles = sorted({
    t["author"]["screen_name"]
    for t in tweets
    if t.get("author", {}).get("screen_name")
})
print(len(handles), "candidate handles")

You can also build the list from the follower scraper when you want one specific account’s audience, then filter it down by follower count before you spend a fetch on each one.

Step 2: Fetch each public profile

Fetch each public profile to get the bio and website text, which is where the address sits. A plain requests.get("https://x.com/<handle>") will not work here, because X returns a logged-out login shell instead of the profile, so the fetch has to come through a route that clears that wall.

def fetch_profile(username):
    r = requests.get(f"{API}/twitter/profile",
                     params={"username": username, "api_key": KEY}, timeout=30)
    r.raise_for_status()
    return r.json()["data"]

This returns the profile as JSON: display name, bio, website, location, and follower counts, with the login wall handled server-side. The profile scraper documents the full field set.

Step 3: Extract the email with a regex

Extract the email with a regular expression that scans the bio, website, and location text for an address pattern. Add a second pass that undoes the common “name at domain dot com” obfuscation people use to dodge exactly this kind of scan.

import re

EMAIL_RE = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
OBFUSCATED_RE = re.compile(
    r"([A-Za-z0-9._%+-]+)\s*(?:\[at\]|\(at\)|\bat\b)\s*"
    r"([A-Za-z0-9.-]+)\s*(?:\[dot\]|\(dot\)|\bdot\b)\s*([A-Za-z]{2,})",
    re.I,
)

def find_email(profile):
    text = " ".join(filter(None, [
        profile.get("bio"), profile.get("website"), profile.get("location"),
    ]))
    text = OBFUSCATED_RE.sub(r"\1@\2.\3", text)  # "kit [at] site [dot] com" -> kit@site.com
    hits = EMAIL_RE.findall(text)
    return hits[0] if hits else None

Loop the fetch and the parse over your handles, and you have a lead row per profile:

leads = []
for h in handles:
    p = fetch_profile(h)
    leads.append({
        "handle": h,
        "name": p.get("name"),
        "followers": p.get("followers_count"),
        "email": find_email(p),
    })

with_email = [row for row in leads if row["email"]]
print(f"{len(with_email)} of {len(leads)} profiles had a public email")

When a profile has no address in its fields, the email is usually on the contact page of the site in its website field, which is a second scrape against that URL rather than against X. Whatever the source, the address is a candidate, not a confirmed inbox, so the real work starts once you need this to run on thousands of profiles without getting blocked.

How do you scrape Twitter emails at scale without getting blocked?

You scrape Twitter emails at scale without getting blocked by sending each profile to a managed scraper API, so the guest tokens, proxy rotation, and the logged-out wall are handled on the server instead of in your code. This is the route I use past a few hundred profiles, because that is where a self-built fetcher starts failing on X’s defenses.

The block triggers are specific and they stack up fast. A guest token is tied to your IP and expires in a few hours, the internal query IDs X uses rotate every few weeks, datacenter IPs get blocked almost on contact, and a clean residential IP still hits a ceiling around 300 requests per hour. The ChocoData API absorbs all of that, so the same request runs whether you send ten profiles or ten thousand.

The request is a plain GET with your key as a query parameter. For a quick check I use curl:

curl "https://chocodata.com/api/v1/twitter/profile?username=nasa&api_key=$CHOCO_API_KEY"

To process a whole list and write a clean CSV for your outreach tool, batch the fetch-and-parse from the previous section:

import csv, time

def scrape_emails(handles, out="x_leads.csv"):
    rows = []
    for h in handles:
        try:
            p = fetch_profile(h)
            email = find_email(p)
        except Exception as e:
            print(f"skip {h}: {e}")
            continue
        if email:
            rows.append({"handle": h, "name": p.get("name"),
                         "email": email, "website": p.get("website")})
        time.sleep(0.2)  # be gentle even though the API handles rotation
    with open(out, "w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=["handle", "name", "email", "website"])
        w.writeheader()
        w.writerows(rows)
    return rows

ChocoData charges per successful profile fetched, so your real cost per email depends on how many profiles carry an address, which is another reason to point the list at engaged accounts. If you would rather not write any of this, no-code tools return a finished CSV instead. Either way, the addresses still need verifying before you send.

How do you verify the emails you scrape from Twitter?

You verify the emails you scrape from Twitter by checking each address for valid syntax, a live mail domain, and a reachable mailbox before you add it to any send. A bio email is often stale, role-based, or mistyped, and sending to a bad address hurts your domain reputation, so verification is not optional if you plan to email the list.

Run three checks in order, cheapest first:

import dns.resolver

def has_mx(email):
    domain = email.rsplit("@", 1)[-1]
    try:
        return bool(dns.resolver.resolve(domain, "MX"))
    except Exception:
        return False

In my testing, engaged and keyword-matched audiences returned deliverable emails on the high-80s to low-90s percent of profiles that carried any address, while a random follower dump landed far lower, because most accounts publish no email at all. Deduplicate across sources, tag each address with where it came from, and warm a new sending domain slowly. None of that matters, though, if you are not allowed to collect or send in the first place.

Scraping emails from Twitter (X) is legally risky, and the risk splits across three layers: US scraping law, X’s own contract, and the data-protection rules that govern collecting and emailing a real person. I am not a lawyer and this is not legal advice, but every source points to the same three-part picture.

On the collection side, US courts have generally held that scraping public data does not by itself break the Computer Fraud and Abuse Act, the line that runs through the hiQ v. LinkedIn cases. X’s contract is stricter, though: its Terms of Service prohibit scraping without prior written consent and set liquidated damages of $15,000 for any party that accesses more than 1,000,000 posts in a 24-hour period by automated means. I walk through both layers in detail in is scraping Twitter legal.

The email layer adds data-protection law on top. Under the GDPR, an email tied to a person is personal data even when it is publicly visible, so collecting and processing the addresses of EU residents needs a lawful basis, per the UK regulator’s guidance on lawful basis. Sending is governed separately: in the US the CAN-SPAM Act requires a working opt-out on every message and treats harvesting addresses from a site that forbids it as an aggravating factor.

The practical takeaway is that the exposure attaches to scraping and emailing real people, not to the choice of tool. Get consent where your jurisdiction needs it, honor opt-outs, and stay inside GDPR if any target is in the EU. If you are weighing which route clears that bar with the least overhead, I compared the managed and DIY options in the best Twitter scrapers of 2026.

FAQ

Does the X (Twitter) API return user email addresses?

No. The X API returns a confirmed email only for the authenticated account, the one whose token you are using, and there is no endpoint that turns another user's handle or ID into their email. That limit is tied to privacy and data-protection rules, and it holds at every paid tier, so an email scraper reads the public profile instead of asking the API for an address.

Can you scrape emails from a Twitter account's followers?

Yes. You pull the follower list, then fetch each follower's profile and parse the bio and website for an email. The catch is yield: a raw follower dump from a large account returns a low share of public emails, because most personal accounts publish none. In my testing, engaged or keyword-matched audiences returned deliverable emails far more often, so I build lists from a keyword search or from the accounts that engaged with a relevant tweet.

How do you scrape emails from Twitter without writing code?

Use a no-code Twitter email tool that takes a username, keyword, or tweet URL and returns a verified-email CSV. These handle the fetch and the parsing for you, at the cost of a per-credit price and no pipeline to fold into your own code. I ranked the ones that held up in my best Twitter email scrapers comparison.

What email success rate should you expect when scraping X profiles?

Expect the deliverable-email rate to track your list quality, not the tool. On engaged and keyword-matched audiences I saw deliverable emails on the high-80s to low-90s percent of profiles that had any public address, and far lower on a random follower dump, because most accounts publish no email at all. Verify every address before sending, since a bio email is often role-based or stale.

KM
Kit Mason
I've built X data pipelines for years. On twitterscraperapi.com I run X scraping methods against live pages and publish what actually holds up.