# Username Account Scraper (`automation-lab/cross-platform-username-discovery`) Actor

Check usernames across GitHub, GitLab, and Mastodon and export matched public profile URLs, account states, and visible metadata for OSINT research.

- **URL**: https://apify.com/automation-lab/cross-platform-username-discovery.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Social media, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Username Account Scraper

Check a supplied **username account** across GitHub, GitLab, and Mastodon, then export matched public profile URLs, platform identity, account state, and visible metadata. The Actor turns repeatable OSINT account discovery into structured JSON, CSV, Excel, or API output.

It is designed for focused investigations on three developer and federated-social platforms. It does not claim to search every website, infer that accounts belong to the same person, recover credentials, or access private data.

### What does Username Account Scraper do?

For each supplied username, the Actor can:

- look up the corresponding public GitHub account;
- find an exact public GitLab username;
- check one or more public Mastodon instances;
- accept `name@instance.social` for a precise Mastodon lookup;
- normalize identity, profile, biography, count, and timestamp fields;
- preserve an optional `not_found` audit row for every completed check;
- stop on upstream errors instead of reporting a false absence;
- export results through the default Apify dataset.

The implementation uses the platforms' public account APIs. It does not open browsers or use residential proxies.

### Who is this username account finder for?

#### Trust and safety analysts

Check a reported handle against developer and federated platforms and retain source URLs for review.

#### Fraud and abuse investigators

Build an initial public account footprint before deeper, authorized research. A match is a lead, not proof of common ownership.

#### Open-source intelligence researchers

Schedule the same bounded account checks and compare exported records in a database or spreadsheet.

#### Developer relations and security teams

Resolve public GitHub and GitLab identities, repository counts, visible biographies, and canonical profile links.

### Supported platforms

| Platform | Lookup scope | Useful public fields |
| --- | --- | --- |
| GitHub | `github.com` public API with a rate-aware public profile fallback | Profile URL, public identity, avatar, location, website, followers, following, and repositories; API-only fields can be null during fallback |
| GitLab | `gitlab.com` exact username query | Profile URL, name, state, bio, location, website, avatar, visible activity dates |
| Mastodon | User-selected public instances | Profile URL, display name, account state, bio, website field, followers, following, creation and latest-status dates |

Mastodon does not have a global username namespace. `alex@mastodon.social` and `alex@fosstodon.org` are different accounts. For a plain username, the Actor checks every entry in `mastodonInstances`.

### What data is extracted?

| Field | Meaning |
| --- | --- |
| `username` | Canonical platform username, or checked value for a not-found row |
| `platform` | `github`, `gitlab`, or `mastodon` |
| `instance` | Mastodon server hostname; null for GitHub and GitLab |
| `found` | Whether a matching public account was returned |
| `accountState` | Visible state such as `active`, `blocked`, `suspended`, or `not_found` |
| `accountId` | Public platform identifier when available |
| `profileUrl` | Canonical public profile link |
| `displayName` | Public display name |
| `bio` | Visible biography; Mastodon HTML is converted to text |
| `avatarUrl` | Public avatar image URL |
| `location` | Visible location when provided |
| `website` | Visible profile website or homepage |
| `followersCount` | Public follower count when exposed |
| `followingCount` | Public following count when exposed |
| `repositoriesCount` | Public repository count, currently available for GitHub |
| `createdAt` | Visible account creation timestamp or date |
| `lastActivityAt` | Latest visible profile update, activity, or status date |
| `sourceUrl` | Public API URL used for the check |
| `checkedAt` | UTC timestamp for the completed lookup |

Fields unsupported or hidden by a platform are returned as null. Counts and profile metadata are observations from the check time and may change later.

### How to find username accounts

1. Open the Actor in Apify Console.
2. Add one or more values to **Usernames**.
3. Select GitHub, GitLab, Mastodon, or any combination.
4. If checking Mastodon, keep `mastodon.social`, add other instances, or use `name@instance` inputs.
5. Enable **Include accounts not found** only when your audit needs completed-check evidence.
6. Set **Maximum saved results** to bound dataset output.
7. Click **Start**.
8. Open the Dataset tab and export JSON, CSV, Excel, XML, or RSS.

A useful first input is:

```json
{
  "usernames": ["octocat", "Gargron@mastodon.social"],
  "platforms": ["github", "gitlab", "mastodon"],
  "mastodonInstances": ["mastodon.social"],
  "includeNotFound": false,
  "maxResults": 20
}
```

### Input parameters

| Input | Type | Default | Description |
| --- | --- | --- | --- |
| `usernames` | string array | required | One to 100 names. A leading `@` is accepted. Use `name@instance` for one explicit Mastodon server. |
| `platforms` | string array | all three | Platforms to check. Values are `github`, `gitlab`, and `mastodon`. |
| `mastodonInstances` | string array | `mastodon.social` | Up to 10 public server hostnames used for plain usernames. |
| `includeNotFound` | boolean | `false` | Save an audit row for checks that completed without a match. |
| `maxResults` | integer | `20` | Save at most 1–1,000 result rows after completing requested checks. |

A run can request at most **100 combined platform checks**. Each username/platform pair is one check, and each Mastodon username/instance pair is one check. Inputs above that hard limit fail before any API request or start charge. Duplicate input values and platform selections are removed. Invalid usernames, invalid Mastodon hostnames, unexpected API responses, and exhausted transient retries also fail clearly.

### Output example

A current GitHub match has this normalized shape (some values shortened for readability):

```json
{
  "username": "octocat",
  "platform": "github",
  "instance": null,
  "found": true,
  "accountState": "active",
  "accountId": "583231",
  "profileUrl": "https://github.com/octocat",
  "displayName": "The Octocat",
  "bio": null,
  "avatarUrl": "https://avatars.githubusercontent.com/u/583231?v=4",
  "location": "San Francisco",
  "website": "https://github.blog",
  "followersCount": 20000,
  "followingCount": 9,
  "repositoriesCount": 8,
  "createdAt": "2011-01-25T18:44:36Z",
  "lastActivityAt": "2025-11-22T12:23:27Z",
  "sourceUrl": "https://api.github.com/users/octocat",
  "checkedAt": "2026-08-14T14:20:00.000Z"
}
```

Live counts and timestamps can differ. Exported rows always follow the dataset schema available in the run.

### How much does it cost to find username accounts?

The Actor uses pay per event:

- **Start:** $0.005 once per accepted run, covering up to the hard limit of 100 platform checks, including checks that return no match.
- **Username account record:** tiered by your Apify plan. The BRONZE reference price is $0.010324 per saved matched or requested not-found row.

At the BRONZE tier, calculate a run as the $0.005 start event plus the number of saved records multiplied by the $0.010324 record event. For example, a five-record run consists of one start event and five record events. A 100-check run with no matches and `includeNotFound=false` costs only the $0.005 start event because the bounded lookup work is included in that event. Your active Apify subscription determines the exact record tier.

Checks with no match are omitted unless the not-found audit option is enabled. Every saved dataset row uses the username account record event; unsaved not-found checks have no separate event charge because every run is hard-capped at 100 checks and the start event covers that bounded work.

### Tips for reliable OSINT account discovery

- Use exact known handles rather than names with spaces.
- Add only Mastodon instances relevant to the investigation.
- Use `name@instance` when the server is known; it avoids unnecessary instance checks.
- Keep `includeNotFound` off for a matches-only lead list.
- Enable it for recurring audits where absence evidence is important.
- Store `checkedAt` with every comparison; public profiles change.
- Treat matching handles as hypotheses. Confirm ownership with independent evidence.
- Split very large recurring lists into bounded runs to stay within anonymous API limits.

### Scheduling and change monitoring

Apify schedules can run the same input hourly, daily, weekly, or monthly. Send each completed dataset to your own table, warehouse, or webhook and compare stable keys such as:

```text
platform + instance + username
```

Useful changes include a newly found account, an account state change, a profile URL change, or updated visible counts. The Actor does not maintain history or send change alerts itself; Apify schedules and integrations provide the automation trigger.

### Export and integration workflows

#### Spreadsheet review

Export the dataset as CSV or Excel, then filter by `platform`, `found`, or `accountState`.

#### Trust and safety queue

Send matched records to a webhook and route profile URLs to an analyst. Keep the Actor's result as public-source evidence, not an automated identity verdict.

#### Data warehouse

Append rows by `checkedAt`, then build a latest-state view keyed by platform, instance, and username.

#### Make, Zapier, and n8n

Use the Apify integration to start a task, wait for completion, and consume dataset items in downstream steps.

### Run with the Apify API

Replace `YOUR_TOKEN` with an Apify API token stored securely.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~cross-platform-username-discovery/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "usernames": ["octocat"],
    "platforms": ["github", "gitlab", "mastodon"],
    "mastodonInstances": ["mastodon.social"],
    "maxResults": 10
  }'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/cross-platform-username-discovery').call({
  usernames: ['octocat', 'Gargron@mastodon.social'],
  platforms: ['github', 'gitlab', 'mastodon'],
  mastodonInstances: ['mastodon.social'],
  includeNotFound: false,
  maxResults: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/cross-platform-username-discovery').call(run_input={
    'usernames': ['octocat'],
    'platforms': ['github', 'gitlab', 'mastodon'],
    'mastodonInstances': ['mastodon.social'],
    'includeNotFound': False,
    'maxResults': 10,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

For production automation, start runs asynchronously and inspect terminal status before reading the dataset.

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/cross-platform-username-discovery"
```

#### Claude Desktop setup

Add this server object to the Claude Desktop MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/cross-platform-username-discovery"
    }
  }
}
```

#### Cursor setup

Open **Settings → MCP → Add server**, choose HTTP, and use:

```text
https://mcp.apify.com?tools=automation-lab/cross-platform-username-discovery
```

#### VS Code setup

Add the same HTTP URL as an MCP server in your workspace or user MCP configuration, then authorize Apify when prompted.

Example prompts:

- “Check `octocat` on GitHub, GitLab, and mastodon.social. Return only matched profile URLs.”
- “Create an audit dataset for `torvalds` and `Gargron@mastodon.social`, including not-found checks.”
- “Run the username account lookup weekly and summarize profile-state changes from my stored datasets.”

### Limits and failure behavior

- Coverage is limited to GitHub.com, GitLab.com, and the requested Mastodon instances.
- GitHub and GitLab self-managed installations are not supported.
- Mastodon servers may be offline, private, blocked, or API-incompatible.
- Anonymous public APIs enforce their own rate limits. If GitHub's API returns HTTP 403 or an exhausted HTTP 429, the Actor makes one bounded lookup through the corresponding public profile page.
- A blocked or malformed GitHub fallback fails the run and is never reported as `not_found`; only a real HTTP 404 is classified as absent.
- Persistent GitLab/Mastodon rate limits, server errors, malformed responses, or unexpected content types fail the run.
- The Actor retries transport, 429, and selected temporary 5xx errors twice with bounded backoff.
- The Actor does not use a proxy, login, CAPTCHA service, browser, or private API.
- `maxResults` limits saved rows, not requested checks; the selected usernames, platforms, and Mastodon instances must independently produce no more than 100 checks.
- Inputs above 100 combined checks fail before any public API request or start charge.
- Public APIs can omit fields or expose platform-specific date precision.

### Responsible and legal use

This Actor accesses public account endpoints. Use it only for lawful purposes and follow platform terms, applicable privacy law, employment rules, and organizational policies.

Do not use a shared username as proof that accounts belong to one person. Do not use the output for harassment, stalking, credential attacks, password recovery, impersonation, or decisions that require verified identity. Apply data minimization, access controls, retention limits, and human review to investigation datasets.

You are responsible for the input, processing purpose, downstream use, and deletion policy.

### Troubleshooting

#### Why did the run fail instead of returning not found?

A documented absence is different from an upstream error. The Actor returns `not_found` only when the platform completed the lookup with its normal absence response. Rate limits, server failures, invalid JSON, and incompatible instances fail closed so they are not mistaken for evidence that an account does not exist.

#### Why was a Mastodon account not found?

Confirm the server. A plain username checks only `mastodonInstances`; it does not search every federated server. Prefer `name@instance.social` when you know the full handle.

#### Why is a GitLab field null?

GitLab's anonymous public user search returns a smaller field set than authenticated endpoints. The Actor reports only what the public response provides.

#### Can this find a Gmail account or password?

No. Search demand sometimes associates “username account” with Gmail, passwords, or recovery, but those are unsupported and unsafe equivalences. This Actor only checks the three documented public profile platforms.

#### Does a matching handle prove common ownership?

No. Usernames can be shared, copied, recycled, or impersonated. Use profile content and independent authorized evidence before drawing conclusions.

### FAQ

#### Does the Actor need credentials?

No. It uses anonymous public endpoints. GitHub API rate pressure activates a bounded public profile-page fallback, while blocked lookups fail closed rather than becoming false not-found results.

#### Can I check multiple Mastodon instances?

Yes. Add up to 10 hostnames to `mastodonInstances`. An explicit `name@instance` input checks only that instance for Mastodon while still using `name` for selected GitHub and GitLab checks.

#### Are not-found rows charged?

Only when the not-found audit option is enabled. Every saved dataset record uses the `profile` event, whether it is a match or an explicitly requested audit row. Unsaved not-found checks are covered by the one-time start event and the hard limit of 100 platform checks per run.

#### Can I export CSV or Excel?

Yes. Open the default dataset and choose JSON, CSV, Excel, XML, or RSS.

#### Does it download avatars?

No. The Actor stores the public avatar URL but does not download image files.

#### Can I search more than 100 usernames?

One run accepts up to 100. Split larger lists into bounded tasks or API calls and merge results downstream.

### Related automation-lab Actors

- [Social Media Profile Finder](https://apify.com/automation-lab/social-media-profile-finder) extracts social links already published on supplied websites.
- [Phone Number OSINT Scanner](https://apify.com/automation-lab/phone-number-osint-scanner) normalizes authorized phone numbers and gathers public footprint signals.
- [GitHub Contributors Scraper](https://apify.com/automation-lab/github-contributors-scraper) exports public repository contributor records for developer-community research.

Use this Actor when the starting point is a username. Use Social Media Profile Finder when the starting point is a website, and use source-specific Actors when the investigation begins with a repository or phone number.

### Support

For a reproducible problem, include the run ID, sanitized input, affected platform or Mastodon instance, expected behavior, and observed status. Do not include passwords, tokens, private account data, or unnecessary personal information.

# Actor input Schema

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

One to 100 usernames. Use name@instance.social to target a specific Mastodon server; a leading @ is optional. The selected usernames, platforms, and instances must produce no more than 100 combined checks.

## `platforms` (type: `array`):

Public platforms to check. GitHub and GitLab use their main public services; Mastodon uses the configured instances.

## `mastodonInstances` (type: `array`):

Servers used for plain usernames. Explicit name@instance.social inputs override this list. Mastodon usernames are instance-specific, and all selected checks combined are capped at 100 per run.

## `includeNotFound` (type: `boolean`):

Save a not\_found audit row for every checked platform or Mastodon instance without a matching public account.

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

Maximum matched and optional not-found records saved after the requested checks complete. This output limit does not increase the hard cap of 100 platform checks per run.

## Actor input object example

```json
{
  "usernames": [
    "octocat",
    "Gargron@mastodon.social"
  ],
  "platforms": [
    "github",
    "gitlab",
    "mastodon"
  ],
  "mastodonInstances": [
    "mastodon.social"
  ],
  "includeNotFound": false,
  "maxResults": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset containing matched profiles and optional not-found audit records.

# 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": [
        "octocat",
        "Gargron@mastodon.social"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/cross-platform-username-discovery").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": [
        "octocat",
        "Gargron@mastodon.social",
    ],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/cross-platform-username-discovery").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": [
    "octocat",
    "Gargron@mastodon.social"
  ],
  "maxResults": 20
}' |
apify call automation-lab/cross-platform-username-discovery --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/cross-platform-username-discovery"
        }
    }
}

```

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/fR3nfPZEWNtXTKD2Y/builds/YMd2yrbxWLoGSlXHX/openapi.json
