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:

SourceWhere it livesHit rateCost
Public profile email fieldGET /users/{login}36%0 extra API calls
Commits in the repo we found them throughGET /repos/{owner}/{repo}/commits?author={login}50%1 core call
Commits in their own recently-pushed reposGET /repos/{owner}/{repo}/commits?author={login}4%1–4 core calls
Public GPG key user IDsGET /users/{login}/gpg_keys4%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 noreply forms above, rejected unconditionally.
  • Machine accountsdependabot, github-actions, renovate, web-flow, semantic-release-bot and dozens more. A bare bot token anywhere in the local part is the reliable signal; a localpart-only blocklist misses things like bot@some-company.cloud.
  • Non-routable domains.local, .lan, .internal, .invalidand friends, which leak out of a laptop’s git config as things like someone@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?

93%
Contactable, combined
Four stages, early exit, on 76 users sampled from language:go topic:kubernetes stars:>100
2.0
API calls per person
Versus ~7 for a full sweep of every stage
10.1/sec
Throughput
At concurrency 8 against one token
29%
Privacy-protected addresses
Of emails found in commit metadata, 197 GitHub users

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.

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