# GitHub Gmail Scraper (`parsebird/github-gmail-scraper`) Actor

Find GitHub developer emails, including Gmail addresses, from public profiles and commits. Search by username, organization, repository or keyword. Export JSON, CSV, Excel or use the API.

- **URL**: https://apify.com/parsebird/github-gmail-scraper.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.19 / 1,000 leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### GitHub Gmail Scraper

GitHub Gmail Scraper finds the public email addresses of GitHub developers, including Gmail and work addresses, from their profiles and public commits.

<table><tr>
<td style="border-left:4px solid #1C1917;padding:12px 16px;font-weight:600">
Build developer lists from usernames, organizations, repositories or a GitHub user search, and get each person's email, email source, company, location, website, followers in 22 structured fields.
</td>
</tr></table>

##### Copy to your AI assistant

Copy this block into ChatGPT, Claude, Cursor, or any LLM to start using this actor.

```text
Actor ID: parsebird/github-gmail-scraper. Python: from apify_client import ApifyClient; client = ApifyClient("<APIFY_TOKEN>"); run = client.actor("parsebird/github-gmail-scraper").call(run_input={"usernames": ["torvalds", "gvanrossum"], "maxResults": 2, "enrichEmailFromCommits": True}); items = client.dataset(run["defaultDatasetId"]).list_items().items. Inputs: usernames (string[], default []), orgs (string[], default []), repos (string[] as owner/repo, default []), searchQuery (string, GitHub user search syntax, default ""), maxResults (integer >= 1, default 100, total across inputs), enrichEmailFromCommits (boolean, default true), githubToken (secret string, optional, needed for emails set on profiles), proxyConfiguration (object, default {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}), disableUsageStats (boolean, default false). Output fields: type, login, name, html_url, email, email_source (profile|commit|none), company, location, blog, twitter_username, hireable, bio, followers, following, public_repos, public_gists, account_created_at, profile_updated_at, query, scraped_at, parse_confidence, warnings. API spec: https://apify.com/parsebird/github-gmail-scraper/api/openapi. Get a token: https://console.apify.com/account/integrations.
```

### What is GitHub Gmail Scraper?

**GitHub Gmail Scraper** is a GitHub email scraper and an alternative to calling the [GitHub REST API](https://docs.github.com/en/rest) yourself. Give it GitHub usernames, organizations, repositories or a user search, and it returns **one structured lead per person**, with the **public email** when one can be found.

The easiest way to try it is to press **Start** with the two example usernames. Results appear in a table you can download as JSON, CSV or Excel.

The Actor returns any public address it finds, so results include Gmail, company and personal-domain emails. To keep Gmail only, filter the `email` column for `@gmail.com` after the run.

### What can GitHub Gmail Scraper do?

- 🔎 **Four input modes:** direct usernames, public organization members, repository contributors, and GitHub user search. Combine them in one run.
- 📬 **Commit email lookup:** when a profile shows no email, the Actor checks that person's public commits and keeps only addresses on commits GitHub links to their account.
- 🧹 **Clean leads:** duplicates are removed case-insensitively, organizations and bots are left out, and GitHub `noreply` addresses and machine-generated hostnames are never returned as emails.
- 🏷️ **Traceable results:** every lead shows where its email came from (`email_source`) and which input found it (`query`).

Because it runs on the Apify platform, you also get:

- [Scheduling](https://docs.apify.com/platform/schedules) for daily or weekly refreshes
- [API access](https://docs.apify.com/api/v2) and client libraries for Python and JavaScript
- [Integrations](https://docs.apify.com/platform/integrations) with tools like Make, Zapier, Slack and Google Sheets
- Exports in JSON, CSV, Excel, XML and HTML

### What data can you extract from GitHub?

| Field | Description |
|-------|-------------|
| `login`, `name`, `html_url` | GitHub username, display name and profile URL |
| `email` | Public email address, or `null` if none was found |
| `email_source` | `profile`, `commit` or `none` |
| `company`, `location`, `bio` | Details from the profile |
| `blog`, `twitter_username` | Website and X username |
| `hireable` | GitHub's "available for hire" flag, `true` or `null` |
| `followers`, `following`, `public_repos`, `public_gists` | Public account metrics |
| `account_created_at`, `profile_updated_at` | Account dates |
| `query` | Input that found this person: `username`, `org:<name>`, `repo:<owner/repo>` or `search:<query>` |
| `scraped_at` | UTC time the record was created |
| `parse_confidence`, `warnings` | Quality signals (see the FAQ) |

### How to scrape GitHub emails

1. Open [GitHub Gmail Scraper](https://apify.com/parsebird/github-gmail-scraper) and go to the Input tab.
2. Fill in any mix of **Usernames**, **Organizations**, **Repositories** and **User search query**, for example `location:berlin language:python`. See [GitHub's user search syntax](https://docs.github.com/en/search-github/searching-on-github/searching-users).
3. Set **Max results**, the total number of leads across all inputs.
4. Leave **Find emails in public commits** on to look for emails hidden from profiles.
5. Optionally add a **GitHub token** under Advanced. It's the only way to read emails people have set on their profiles (see the FAQ).
6. Press **Start**, then open the results and check `email_source` and `warnings`.
7. Download the dataset, or repeat the run with [Apify Schedules](https://docs.apify.com/platform/schedules). Weekly is a reasonable starting point.

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| usernames | array | No | `[]` | GitHub usernames or profile URLs |
| orgs | array | No | `[]` | Organizations; each public member is added |
| repos | array | No | `[]` | `owner/repo` or repository URLs; contributors are added |
| searchQuery | string | No | `""` | GitHub user search query |
| maxResults | integer | No | 100 | Maximum leads across all inputs, at least 1 |
| enrichEmailFromCommits | boolean | No | `true` | Check public commits when the profile shows no email |
| githubToken | string (secret) | No | none | Token for 5,000 requests per hour and profile emails; needs no scopes for public data |
| proxyConfiguration | object | No | Residential | Proxy for GitHub requests. With a token and this default, requests go direct; a changed setting is used as supplied |
| disableUsageStats | boolean | No | `false` | Skip the `USAGE_STATS` record in this run's key-value store |

Provide at least one of `usernames`, `orgs`, `repos` or `searchQuery`.

```json
{
  "usernames": ["torvalds", "gvanrossum"],
  "maxResults": 2,
  "enrichEmailFromCommits": true,
  "disableUsageStats": false
}
```

### Output example

Real records from a test run, with the email addresses hidden here. Download results in JSON, CSV, Excel, XML or HTML.

```json
[
  {
    "type": "user",
    "login": "gvanrossum",
    "name": "Guido van Rossum",
    "html_url": "https://github.com/gvanrossum",
    "email": "<hidden for privacy>",
    "email_source": "commit",
    "company": "Microsoft",
    "location": "San Francisco Bay Area",
    "blog": "https://python.org/~guido/",
    "twitter_username": "gvanrossum",
    "hireable": null,
    "bio": null,
    "followers": 27307,
    "following": 4,
    "public_repos": 29,
    "public_gists": 12,
    "account_created_at": "2012-11-26T18:46:40Z",
    "profile_updated_at": "2026-10-24T03:53:36Z",
    "query": "username",
    "scraped_at": "2026-11-08T06:55:27Z",
    "parse_confidence": 1,
    "warnings": []
  }
]
```

Each run also stores a summary in the `OUTPUT` record of its key-value store, with lead counts plus any skipped or failed inputs.

### Use cases

- Find maintainers of a dependency to report a security issue or ask about licensing
- Reach the contributors of a project about something that concerns their work
- Research who works in an open-source organization or technology community
- Enrich a list of GitHub usernames you already have with public profile details

Read the legal section below before using emails for outreach.

### How it works

1. The Actor turns your inputs into a list of accounts, in the order usernames, organizations, repositories, search, and removes duplicates.
2. It reads each public profile through GitHub's REST API. Organizations and bots are skipped.
3. If the profile shows no usable email and commit lookup is on, it checks up to 3 of the person's recently pushed repositories, their own before forks.
4. It keeps the most frequent valid address on commits linked to that account.
5. It stops at `maxResults`. Inputs are only read as far as needed.

### How much does it cost to scrape GitHub emails?

GitHub Gmail Scraper uses pay-per-event pricing. Compute and proxy costs are included in the event price.

| Event | Free | Bronze | Silver | Gold |
|-------|------|--------|--------|------|
| Actor start (once per run) | $0.005 | $0.005 | $0.005 | $0.005 |
| Lead, per event | $0.00479 | $0.00459 | $0.00439 | $0.00419 |
| **Lead, per 1,000** | **$4.79** | **$4.59** | **$4.39** | **$4.19** |

A lead is one person saved to the dataset, with or without an email. On the Free plan, 1,000 leads cost $4.79 plus the $0.005 start fee. If no users can be resolved, no leads are charged. Set a maximum cost per run in the run options and the Actor stops when it's reached.

### Python and JavaScript API examples

Install the [Apify client](https://docs.apify.com/api/client/python) and call the Actor from your code. Your token is on the [Integrations page](https://console.apify.com/account/integrations).

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("parsebird/github-gmail-scraper").call(run_input={
    "searchQuery": "location:berlin language:python",
    "maxResults": 50,
})

for lead in client.dataset(run["defaultDatasetId"]).iterate_items():
    if lead["email"]:
        print(lead["login"], lead["email"], lead["email_source"])
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('parsebird/github-gmail-scraper').call({
    orgs: ['pallets'],
    maxResults: 50,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((lead) => lead.email));
```

### Limits and edge cases

| Condition | What you get | What to do |
|-----------|--------------|------------|
| No public email found | The lead is returned and charged, with `email: null` and `email_source: "none"` | Keep the row or filter on `email` |
| Only `noreply` addresses found | They aren't used; the warning `only_noreply_emails_found` is added | Treat as no public email |
| User has no public repositories | Commit lookup is skipped; the warning `no_public_repos` is added | Use the profile fields |
| Organization placed in Usernames | It's skipped and not charged | Put it in Organizations |
| Repository isn't `owner/repo` | It's skipped and listed in `OUTPUT` | Use the owner and name separated by one slash |
| No users could be resolved | 0 leads, nothing charged, and `OUTPUT` has `error: "no_users_resolved"` | Check spelling and inputs |
| Profile lookup keeps failing | It's retried with fresh sessions, then listed under `failed` in `OUTPUT` | Add a token and rerun |
| Commit lookup fails after the profile is read | The lead is kept, with the warning `commit_lookup_failed` and `parse_confidence: 0.8` | Rerun that user |
| Search matches over 1,000 accounts | GitHub returns the first 1,000 at most | Narrow the query or split it, for example by location |
| Repository too large for GitHub's contributor list | Authors of up to 500 recent commits are used instead | Add more inputs if you need more people |

### Is it legal to scrape GitHub?

Scraping publicly available data is generally legal, but what you do with it matters, and emails are personal data. Results can fall under regulations such as the [GDPR](https://gdpr.eu/) and laws on unsolicited marketing like CAN-SPAM. Don't process personal data without a legitimate reason, and consult your lawyers if you're unsure. For background, see Apify's post on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

GitHub also sets its own rules. Its [Acceptable Use Policies](https://docs.github.com/en/site-policy/acceptable-use-policies/github-acceptable-use-policies) say you may not use information from the service for spamming purposes, including sending unsolicited emails to users, or for selling personal information, such as to recruiters, headhunters and job boards. You are responsible for following these rules with the data you collect.

### Other lead and email tools from ParseBird

- [Website Email, Phone Numbers & Social Media Profiles Finder](https://apify.com/parsebird/website-contact-finder)
- [TikTok Influencer Email Finder](https://apify.com/parsebird/tiktok-influencer-email-finder)
- [DeviantArt Email Scraper](https://apify.com/parsebird/deviantart-email-scraper)
- [Data Deduplicator](https://apify.com/parsebird/dataset-deduplicator) to merge leads from several runs
- [Data Cleaner](https://apify.com/parsebird/data-cleaner) to tidy exported data

### FAQ

**Does it only return Gmail addresses?**
No. It returns whichever public address it finds, whether that's Gmail, a company domain or a personal domain. Filter the `email` column for `@gmail.com` if you only want Gmail.

**Why do I need a token to get profile emails?**
In our tests, GitHub's API returned no profile email to anonymous requests, even for accounts whose public email matched a GitHub search. Without a token, emails come from public commits only. With a token, the email a person chose to show on their profile is returned as `email_source: "profile"`. Create a [personal access token](https://github.com/settings/tokens); it needs no scopes for public data.

**Why is `email` empty for some people?**
They may have no public commits, commit with GitHub's `noreply` address, or have commits that aren't linked to their account. The Actor doesn't guess addresses.

**How accurate are commit emails?**
A commit email is what the author's Git client recorded when the commit was made, so it can be old. Only addresses on commits GitHub links to that account are used.

**What does `parse_confidence` mean?**
It's `1` when all lookups for the lead finished, and `0.8` when the commit lookup failed, so the lead may be missing an email.

**What is the usage statistics record?**
A `USAGE_STATS` record in this run's own key-value store with counts of inputs, leads and API requests. Your token and results aren't included, and it isn't sent elsewhere. Turn on **Skip the usage statistics record** to leave it out.

**How many leads can I get without a token?**
GitHub allows 60 anonymous requests per hour per IP address, so the Actor rotates proxy sessions. With a token, the limit is 5,000 requests per hour; a lead with commit lookup can take up to 5 requests.

**Can I use it through the API or with AI agents?**
Yes. See the [API tab](https://apify.com/parsebird/github-gmail-scraper/api) and connect it to tools through [integrations](https://docs.apify.com/platform/integrations) or the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp).

**Something isn't working. Where can I report it?**
Open a ticket in the [Issues tab](https://apify.com/parsebird/github-gmail-scraper/issues). Feedback and feature requests are welcome.

# Changelog

This Actor's version history is a separate document: https://apify.com/parsebird/github-gmail-scraper/changelog.md

# Actor input Schema

## `usernames` (type: `array`):

Enter GitHub usernames or profile URLs to look up directly. Organizations are skipped here; put them in Organizations.

## `orgs` (type: `array`):

Enter organization logins or URLs. Every member who shows their membership publicly is added.

## `repos` (type: `array`):

Enter repositories as owner/repo or URLs. Their contributors are added; contributors without a GitHub account are left out.

## `searchQuery` (type: `string`):

Enter a GitHub user search, such as <code>location:berlin language:python</code>. GitHub returns at most 1,000 accounts per query. <a href='https://docs.github.com/en/search-github/searching-on-github/searching-users' target='_blank'>Search syntax</a>.

## `maxResults` (type: `integer`):

Set the maximum number of leads across all inputs combined.

## `enrichEmailFromCommits` (type: `boolean`):

Check the user's public commits when their profile shows no email.

## `githubToken` (type: `string`):

Add a <a href='https://github.com/settings/tokens' target='_blank'>GitHub personal access token</a> to raise the limit to 5,000 requests per hour. It needs no scopes for public data and is stored encrypted.

## `proxyConfiguration` (type: `object`):

Choose the proxy used for GitHub requests. With a token and this default setting, requests go direct; any other setting is used as you set it.

## `disableUsageStats` (type: `boolean`):

Turn on to skip writing the USAGE_STATS record (input counts, lead count, API request count) to this run's key-value store. The record stays in your own storage.

## Actor input object example

```json
{
  "usernames": [
    "torvalds",
    "gvanrossum"
  ],
  "orgs": [],
  "repos": [],
  "searchQuery": "",
  "maxResults": 2,
  "enrichEmailFromCommits": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "disableUsageStats": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "usernames": [
        "torvalds",
        "gvanrossum"
    ],
    "maxResults": 2,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/github-gmail-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "usernames": [
        "torvalds",
        "gvanrossum",
    ],
    "maxResults": 2,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("parsebird/github-gmail-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "usernames": [
    "torvalds",
    "gvanrossum"
  ],
  "maxResults": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call parsebird/github-gmail-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/github-gmail-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/6JtchbGaXst12BENk/builds/ZCHq1OASK4sq8SSK0/openapi.json
