How to find a developer's email address on GitHub
Four public sources yield a developer’s email on GitHub: the profile email field, the author email in public git commit metadata, commits in their own repositories, and public GPG key user IDs. Used in that order and stopping at the first usable address, we reach 93% of a contributor-mined pool at 2.0 API calls per person. Addresses ending in @users.noreply.github.com are never usable and must not be worked around.
By Rahul Vishwakarma, creator of GitFinder · Reviewed 31 August 2026
Which sources actually yield an email?
Every legitimate method reads data the developer chose to publish. There is no hidden endpoint and no private field. In yield-per-API-call order:
| Source | Where it lives | Hit rate | Cost |
|---|---|---|---|
| Public profile email field | GET /users/{login} | 36% | 0 extra API calls |
| Commits in the repo we found them through | GET /repos/{owner}/{repo}/commits?author={login} | 50% | 1 core call |
| Commits in their own recently-pushed repos | GET /repos/{owner}/{repo}/commits?author={login} | 4% | 1–4 core calls |
| Public GPG key user IDs | GET /users/{login}/gpg_keys | 4% | 1 core call |
Hit rates are measured, not estimated — 76 users sampled from `language:go topic:kubernetes stars:>100`, using a production GitHub token. They are cumulative-with-early-exit, so they sum to more than the 93% combined figure: each stage only runs for the people the cheaper stages missed.
A fifth source exists — `search/commits?q=author:{login}` — covers all of GitHub in one call— and scores highest in isolation. We keep it last anyway, because it spends GitHub’s 30-requests-per-minute search budget rather than the 5,000-per-hour core budget. At a hundred candidates per search, search quota is the scarcest resource in the system.
Why the technique in most guides no longer works
Almost every article, tutorial and OSINT tool on this subject recommends the same trick: call GET /users/{login}/events/public, find a PushEvent, and read payload.commits[].author.email. It was elegant — one request, no repository to guess.
It does not work any more. GitHub removed the commits array from PushEvent payloads. Verified live against this project’s own token: every PushEvent payload now contains exactly {before, head, push_id, ref, repository_id} — five scalar fields and no commit objects, so there is no author email to read.
If a guide tells you to read commit emails out of the public events feed, it was written before that payload changed and has not been retested. The replacement is source 2 below: query the commits endpoint of a repository the person actually contributed to.
This matters beyond trivia. A pipeline built on the events feed does not error — it silently returns nothing, and looks like a person with no public email rather than a broken lookup.
1. Does the profile email field still work?
Yes, and it is the first thing to check because it costs nothing extra. GitHub exposes a public email field on the user object, which is populated when someone has set an email as public in their profile settings:
GET https://api.github.com/users/{login} → the email key.
In our sample this was populated for 36% of candidates. That figure is higher than the share you would get across all of GitHub, and the reason is selection: people who maintain or contribute to well-starred repositories publish contact details far more often than the average account. If you sample GitHub at random you should expect substantially less.
2. How do you get an email from commit metadata?
Every git commit carries an author name and email in its object header. When a repository is public, so is that header — which makes it the single highest-yielding source at 50%.
The reliable form is a repository-scoped commit query:
GET /repos/{owner}/{repo}/commits?author={login} → commit.author.email on each returned commit.
The catch is which repository. Guessing costs a call per guess. This is why our pipeline carries the source repository through the whole search: when a candidate was discovered as a contributor to a specific project, we already know a repository they have demonstrably committed to, so the highest-yield stage becomes a single targeted call instead of a hunt.
Practical notes that save time: request 100 commits per page — the API maximum costs the same as 30; expect several distinct addresses across a long history, and prefer the most recent, since a 2016 address is often a dead university mailbox; and ignore the committer field when it is noreply@github.com, which is what GitHub writes for anything merged through the web UI.
3. What if you don't know a repo they contributed to?
Fall back to their own repositories, most-recently-pushed first: GET /users/{login}/repos?sort=pushed&type=owner, then run the commit query above against the first few.
The marginal yield is small — 4% — because most people whose own repos have public commit emails also had a public profile email or were caught by the previous stage. It costs 1–4 calls, so it belongs third, not first. Cap the scan; we stop after three repositories.
4. Are GPG keys a real source?
Yes, and they are the highest-precision one. A GPG key’s user IDs embed the email addresses the key was created for, and GitHub publishes them: GET /users/{login}/gpg_keys → each key’s emails[], with a verified flag.
Coverage is low (4%) because most developers never upload a key. But a GitHub-verified GPG address is the strongest signal available: the person proved control of that mailbox to GitHub. When it exists, prefer it.
Which addresses must you never use?
Anything matching @users.noreply.github.com. Two forms exist: {id}+{login}@users.noreply.github.com for accounts created after July 2017, and {login}@users.noreply.github.com for earlier opt-ins.
That address is the literal artifact of someone turning on “Keep my email address private”. It does not accept mail, so sending to it achieves nothing — and the numeric prefix must never be stripped to guess at a real address. Rejecting it outright is both the correct behaviour and the cheapest compliance win available in this whole exercise: the person has expressed a preference in the clearest way the platform allows.
It is common. In a separate sample of 197 GitHub users while we were building our address filter, 29% of the emails found in commit metadata were noreply addresses. Any tool claiming near-total email coverage on GitHub is either counting these as hits or guessing at addresses.
How do you tell a usable address from noise?
Raw commit metadata is dirty. Four filters, in cost order, remove nearly all of it:
- Privacy artifacts — the
noreplyforms above, rejected unconditionally. - Machine accounts —
dependabot,github-actions,renovate,web-flow,semantic-release-botand dozens more. A barebottoken anywhere in the local part is the reliable signal; a localpart-only blocklist misses things likebot@some-company.cloud. - Non-routable domains —
.local,.lan,.internal,.invalidand friends, which leak out of a laptop’sgit configas things likesomeone@Toms-MacBook-Pro.local. - Role mailboxes and placeholders —
info@,support@,sales@,test@example.com. A recruiting email to a shared inbox is ignored or marked as spam, so these are rejected rather than down-ranked.
Then check the domain actually accepts mail — an MX lookup, with an A record as the RFC 5321 fallback — and cache the result per domain, because candidate addresses cluster hard on a handful of domains.
One thing not to do: publish a confidence percentage you cannot defend. Score provenance and corroboration (a GPG-verified address seen in three recent commits is not the same as one address in a 2015 commit) and be honest that it is an ordering, not a probability.
What hit rate should you actually expect?
Read the 93% with its population attached. It is a pool mined from contributors to well-starred repositories in a specific ecosystem — exactly the population a recruiter searching for a senior engineer ends up in, and exactly the population most likely to have published contact details. Against a random sample of all GitHub accounts, expect far less.
Two other numbers set expectations. GitHub’s core API budget is 5,000 requests per hour per token, and a single realistic sourcing run costs roughly 500 of them — so one token supports around nine full searches an hour, no matter how fast your code is. And on a real run, 3,963 discovered candidates narrowed to 130 screened and 100 with a usable address, in 49 seconds.
Is it legal to email a developer you found this way?
Usually yes, for genuine business-to-business recruiting, but “public” is not the same as “unrestricted” and the rules are not optional. This is a summary, not legal advice.
- Public availability is not consent. Under the GDPR, an email address in a commit header is still personal data. Recruiting outreach is normally run under the legitimate-interests basis (Art. 6(1)(f)), which requires you to have actually done and documented the balancing test — and to stop on request.
- Honour the signal the person gave you. A
noreplyaddress is a stated preference. So is a profile that says “not looking”. Overriding either is where a defensible practice becomes an indefensible one. - US commercial email rules still apply. CAN-SPAM requires accurate headers, a non-deceptive subject line, a working opt-out honoured promptly, and a valid physical postal address in the message.
- One person, one relevant message. Bulk-blasting a scraped list is both the fastest route to a spam complaint and the behaviour that gets sourcing tools blocked.
- Never treat this output as a background check. Screening someone for employment eligibility using compiled third-party data is regulated territory (in the US, the FCRA). Sourcing signals are for deciding who to talk to.
If you are a developer who would rather not appear in GitFinder at all, email info@workonward.com with your GitHub handle. No justification needed and no account needed; the details are in our Privacy Policy.
What breaks when you do this for a hundred people?
Nothing about the four sources changes. What changes is that budget, ordering and cleanup start to dominate:
- Ordering is the whole game. Running every stage for every person costs about seven calls each. Ordering by yield-per-call and exiting at the first usable address costs 2.0 — the difference between roughly nine searches an hour on one token and barely three.
- Two budgets, not one. Core endpoints allow 5,000 requests an hour; the search endpoints allow 30 a minute. A stage that looks cheap in core terms can be the thing that stalls you.
- Cleanup is not a rounding error.Roughly a third of what commit metadata hands you is a privacy artifact, and more is bots and laptop hostnames. Skip the filtering and a “100 contacts” list is perhaps sixty real people.
- Cache by handle, not by search. The same well-known contributors appear across many searches in the same ecosystem.
This is what GitFinder automates end to end — discovery, screening, ranking with reasons, and this email cascade — from a plain-English job description. See how to find developers on GitHub for the discovery half of the problem.
Skip the API plumbing
Paste a job description. GitFinder runs the discovery, the screening, the ranking and this exact email cascade, and hands you a shortlist where every person has a verified, usable address.
Card required · 3 days free, then $49/month plus applicable taxes · Cancel any time in two clicks.
Keep reading
- How to find developers on GitHub — The discovery half: turning a role into a candidate pool.
- GitHub search syntax for sourcing — Every qualifier worth knowing, and which ones filter repos rather than people.
- Pricing — What GitFinder costs, and what a search actually consumes.