EmailGuesser
Email permutation generator and email address guesser for OSINT
You have a name and a domain. You need the inbox. EmailGuesser takes what you know (name, username, birth year, DOB, location, domain) and turns it into a ranked shortlist of the email addresses that person most likely uses, as JSON, CSV or plain text. It runs offline, has no dependencies, and is pure Python. It does the guessing so you only have to check the few that matter.
Quick start · Inputs · CLI · Python API · Output · FAQ · License
See it in action
emailguesser --first John --last Doe --year 1990 --domain gmail.com --limit 6 --category name
| Rank | Pattern | Score | |
|---|---|---|---|
| 1 | john.doe@gmail.com |
{first}.{last} |
0.95 |
| 2 | jdoe@gmail.com |
{first_initial}{last} |
0.90 |
| 3 | johndoe@gmail.com |
{first}{last} |
0.85 |
| 4 | j.doe@gmail.com |
{first_initial}.{last} |
0.80 |
| 5 | doe.john@gmail.com |
{last}.{first} |
0.60 |
| 6 | doej@gmail.com |
{last}{first_initial} |
0.60 |
The tool prints the same data as JSON by default, and as JSONL, CSV or a plain list of addresses on request (see output formats).
Why EmailGuesser
Guessing an email by hand means typing first.last, then flast, then giving up. Online permutators hand you 200 unranked combinations and no idea where to start. Paid finders charge per lookup and send your target's name to a third party.
EmailGuesser sits between the two. People pick addresses from a small set of habits, like first.last, flast, firstlast, or a nickname plus a birth year. EmailGuesser turns those habits into a scored pattern table, applies them to what you know, and ranks the results so the most likely addresses come first.
- Ranked, not dumped. Every candidate carries a 0 to 1 score, so you check the top handful instead of the whole list, which matters when your verification step is rate-limited.
- Private by design. No accounts, no API keys, no network calls. The name you type never leaves your machine.
- Learns a company's format. Know one colleague's address? Pass it with
--known-emailand the right pattern is ranked to the top. - Tuned for real behaviour. Company domains favour
first.last; free-mail domains favour names with years, dates and usernames. - One tool, many formats. Terminal, script or Python library; JSON, JSONL, CSV or a plain address list that pipes straight into the next tool.
- Easy to extend. Patterns, scores, nicknames and domains live in small, readable files, or in a pack you load at runtime.
Features
- Hundreds of email format permutations from name, username, year, DOB, location and domain.
- Builds on every login pattern from NAMINT by The SEINT, with and without number suffixes. A test checks that all of them are still generated.
- Names handled properly: middle names and initials, multi-word and hyphenated names, apostrophes, accents (
José Müllerbecomesjose.muller). - Nickname aware:
Robertalso triesbob,rob,bobby; add your own with--alias. - Date-of-birth aware: suffixes such as
1990,90,1405,140590,14051990. - Username aware: repeatable
--username, with separator variants (j_doe,j.doe,jdoe). - Location aware: your city and country become tokens; the country code also pulls in regional providers (
yahoo.co.in,web.de,mail.ru). - Learns a company format: pass a known address with
--known-emailand matching candidates are ranked to the top. - Custom or default domains: your company domain, or Gmail, Outlook, Yahoo and more.
- Ranked output: every address has a 0 to 1
score, apatternid and acategory. - Batch mode: run a whole JSON, JSONL or CSV file of people in one call.
- Validated and de-duplicated local parts, with optional Gmail dot/plus folding and a per-domain cap.
- Cross-platform: Windows, macOS and Linux, Python 3.9+.
Common use cases
- OSINT and investigations: you have a subject's name and employer. Build a ranked candidate list to check with the breach-lookup, account-discovery or search tools you are authorised to use.
- Sales and recruiting outreach: stop guessing work emails one at a time. Generate the likely address from a name and company domain, or learn the format from one known colleague.
- Penetration testing and red teaming: build realistic target lists for authorised phishing simulations and social-engineering assessments in one command.
- Self-audit and account recovery: work out which address you signed up with years ago.
EmailGuesser only generates guesses. It does not check that an address exists. See the FAQ.
How it works
flowchart LR
A[Your inputs<br/>name, username, year,<br/>DOB, location, domain] --> B[Normalise<br/>accents, hyphens,<br/>nicknames]
B --> C[Build tokens<br/>first, f, last, l,<br/>1990, 90, 1405 ...]
C --> D[Apply pattern table<br/>skip patterns with<br/>missing tokens]
D --> E[Validate and<br/>de-duplicate]
E --> F[Score, sort<br/>and limit]
F --> G[JSON / CSV / TXT]
Installation
Requires Python 3.9 or newer.
pip install emailguesser
For non-Latin names (Cyrillic, Greek, Arabic, CJK), install the optional transliteration extra:
pip install "emailguesser[translit]"
Or clone and run without installing:
git clone https://github.com/OSINTVerseHQ/EmailGuesser.git
cd EmailGuesser
python -m emailguesser --help
After pip install, the emailguesser command is available in your terminal.
Quick start
1. Guess a work email from a name and company domain
emailguesser --first John --last Doe --domain example.com --limit 5 --pretty
2. Learn a company's format from one known address
emailguesser --first Jane --last Smith --known-email "Bob Jones <bjones@acme.com>" --format txt
3. Use everything you know
emailguesser --first John --middle Michael --last Doe --username jdoe \
--year 1990 --dob 1990-05-14 --location "Mumbai, India" \
--domain example.com --domain gmail.com --pretty
4. Get a plain list to pipe into another tool
emailguesser --first John --last Doe --year 1990 --min-score 0.5 --limit 0 --format txt
5. Read input from a JSON file, or run many people at once
emailguesser --input request.json --pretty
emailguesser --batch people.csv --format csv -o candidates.csv
people.csv just needs headers matching the input fields, for example first,last,domain.
6. Generate here, verify elsewhere
EmailGuesser never touches the network, so pair it with a verification step you are authorised to run. The plain list format pipes cleanly into other tools:
emailguesser --first John --last Doe --domain acme.com --format txt --limit 0 > candidates.txt
# then check candidates.txt with a breach-lookup, account-discovery or
# email-verification tool, for example the OSINTverse API (https://osintverse.com).
Input fields
| Field | CLI flag | Required | Notes |
|---|---|---|---|
| First name | --first |
With last name, unless a username is given | Multi-word names work (Mary Ann) |
| Middle name | --middle |
No | Enables middle-name and middle-initial patterns |
| Last name | --last |
With first name, unless a username is given | Hyphenated and multi-word names work (Van Der Berg) |
| Username | --username |
No | Repeatable. A pasted email address is reduced to its local part |
| Alias / nickname | --alias |
No | Repeatable extra first-name form (billg, jdoe) |
| Known email | --known-email |
No | "Name <local@domain>", Name:local@domain, or just local@domain. Learns the domain's format |
| Year | --year |
No | 4 digits (1900 to 2100) |
| Date of birth | --dob, --dob-format dmy|mdy|ymd |
No | 1990-05-14 is read as ISO; other formats follow --dob-format (default dmy) |
| Location | --location |
No | "City, Country" or "City, CC". The country code also adds regional providers |
| Domain | --domain |
No | Repeat or comma-separate. If omitted, popular free-mail providers are used |
You need at least a first and last name, or a username.
Command-line reference
| Option | Description |
|---|---|
--format json|jsonl|txt|csv |
Output format. Default json |
--limit N |
Maximum results. Default 50. Use 0 for unlimited |
--max-per-domain N |
Cap results per domain (0 = no cap) |
--min-score X |
Drop results scoring below X (0 to 1) |
--category C |
Only include one pattern category: name, personal, username, location. Repeatable |
--gmail-dots |
Treat Gmail dot and +tag variants as the same address when de-duplicating |
--input FILE |
Read input fields (and options) from a JSON file. - reads stdin. CLI flags override |
--batch FILE |
Run many inputs from a JSON array, JSONL or CSV file. - reads stdin |
--explain |
Add a per-address score breakdown (json/jsonl output) |
--pack FILE |
Custom pattern pack (JSON/TOML) adding patterns, nicknames and domains |
--calibration FILE |
Score→probability table (see below) adding a probability field |
--provider-aware |
(online) Detect each domain's mail provider by MX; fold dot-variants on Google-hosted domains |
--verify mx|smtp |
(online) Check deliverability. Needs the [verify] extra. Authorised use only |
--drop-undeliverable |
With --verify, drop addresses that are clearly undeliverable |
-o, --output FILE |
Write output to a file instead of stdout |
--pretty |
Indent JSON output |
--version |
Print the version and exit |
Errors go to stderr as {"error": "..."} and the exit code is 2.
Learn a company format, verify, explain
# Learn acme.com's convention from one known colleague, then rank Jane's address
emailguesser --first Jane --last Smith --known-email "Bob Jones <bjones@acme.com>" --format txt
# (online) keep only addresses whose domain can receive mail
emailguesser --first Jane --last Smith --domain acme.com --verify mx --drop-undeliverable
# see exactly why each address scored what it did
emailguesser --first John --last Doe --domain acme.com --explain --pretty
Verification and provider detection are opt-in, make network requests, and need
the extra: pip install "emailguesser[verify]". Use them only against domains
you are authorised to probe.
Calibrated probabilities
A score ranks candidates; it is not itself the chance an address is real. Fit a
score→probability table from your own verified data and apply it:
python -m emailguesser.calibrate your_data.csv -o table.json # needs a true_email column
emailguesser --first John --last Doe --calibration table.json # adds a "probability"
Custom pattern packs
Add patterns, nicknames and domains without editing source (see
examples/pack.example.json):
emailguesser --first John --last Doe --pack mypack.json
Run it anywhere
pipx install emailguesser # isolated CLI
docker run --rm emailguesser --first John --last Doe --domain acme.com
python scripts/build_pyz.py && python dist/emailguesser.pyz --help # single file, no install
A static, backend-free browser version lives in web/ (runs the
generator in-browser via Pyodide).
Python API
from emailguesser import generate
result = generate(
first_name="John",
middle_name="Michael",
last_name="Doe",
usernames=["jdoe"],
aliases=["johnny"],
year="1990",
dob="1990-05-14",
location="Mumbai, India",
domains=["example.com", "gmail.com"],
known_emails=["Bob Jones <bjones@example.com>"],
limit=20,
max_per_domain=0,
)
for item in result["emails"]:
print(item["score"], item["email"])
generate() also accepts a dictionary or an Inputs object as its first argument, plus the CLI options as keyword arguments: limit, min_score, max_per_domain, categories, gmail_dots, dob_format. It returns a plain, JSON-serialisable dict. To render it, use emailguesser.output.render(result, "csv") or render_batch([...], "jsonl").
Output formats
- json (default): the full result object below.
- jsonl: one JSON email object per line, ready for
jqand line-based tools. - txt: one email address per line, highest score first.
- csv:
email,local_part,domain,pattern,category,scorewith a header row.
In batch mode, jsonl and csv gain a leading query column identifying which input produced each row.
JSON output shape
{
"input": {
"first_name": "John", "middle_name": "", "last_name": "Doe",
"usernames": [], "aliases": [], "year": "1990", "dob": "",
"location": {}, "domains": ["gmail.com"], "known_emails": []
},
"meta": {
"count": 6,
"total_candidates": 251,
"truncated": true,
"limit": 6,
"min_score": 0.0,
"max_per_domain": 0,
"domains_used": ["gmail.com"],
"generated_at": "2026-10-18T09:00:00Z",
"warnings": ["no middle name given: middle-name patterns skipped"]
},
"emails": [
{"email": "john.doe@gmail.com", "local_part": "john.doe", "domain": "gmail.com",
"pattern": "{first}.{last}", "category": "name", "score": 0.95}
]
}
| Field | Meaning |
|---|---|
emails[].pattern |
The pattern that produced the address, such as {first}.{last} |
emails[].category |
name, personal, username or location |
emails[].score |
Likelihood estimate from 0 to 1. Results are sorted high to low |
meta.total_candidates |
Addresses found before --limit was applied |
meta.warnings |
Skipped patterns and notes about your input |
How scoring works
score = pattern popularity x domain-type multiplier x domain popularity (capped at 1)
- Pattern popularity:
first.lastranks abovelastf, which ranks abovefirst-m-last. - Domain type: company domains favour plain name patterns; free-mail domains favour name plus number, date and username patterns.
- Name variants: alternative spellings of multi-word names and guessed nicknames are down-weighted.
- Known format: a candidate matching a
--known-emailformat for that domain is boosted to the top.
All weights live in emailguesser/patterns.py and emailguesser/domains.py. After any change, run the calibration harness to see whether ranking of real addresses improved:
python tests/calibration.py
For AI agents
EmailGuesser is built to be driven by agents as well as people. It ships a Model Context Protocol server:
pip install "emailguesser[mcp]" # Python 3.10+
emailguesser-mcp # stdio server; or: python -m emailguesser.mcp
Point your MCP client at the emailguesser-mcp command. It exposes one tool,
guess_emails, and the output JSON Schema as the resource emailguesser://schema.
An agent that can't use MCP can still call the CLI (--format json, or
--format jsonl for batches) or the Python generate() function, and read the
output contract from emailguesser --schema or
schema/result.schema.json. Full guidance, including
the input table and the guardrails to build in, is in AGENTS.md.
FAQ
Does EmailGuesser check whether an email address exists? No. It is offline and generation-only and never contacts a mail server.
How does it relate to NAMINT? EmailGuesser started from NAMINT's login patterns, so it owes that project a lot. NAMINT, by The SEINT, is a browser tool that turns a name into login search patterns with ready-made search links. It's the better choice when you want to search the web for a name, and you can use it right away at seintpl.github.io/NAMINT. EmailGuesser is a Python command-line tool and library focused only on email addresses. It covers every NAMINT login pattern (a test checks this) and adds ranking, nicknames, usernames, date of birth, location, known-format inference, de-duplication and JSON, CSV and TXT output.
Which providers are used by default?
Gmail, Outlook, Yahoo, Hotmail, iCloud, Proton, Live, AOL, MSN and GMX. A location country code adds regional providers. Passing --domain replaces this list.
Why so many Gmail variants?
Gmail ignores dots in the local part. Pass --gmail-dots to collapse j.doe@gmail.com and jdoe@gmail.com into one.
Does it support non-English names?
Accents are converted to ASCII out of the box. For non-Latin scripts, install emailguesser[translit], which transliterates Cyrillic, Greek, Arabic and CJK.
Can I add my own patterns, nicknames or domains?
Yes. Edit emailguesser/patterns.py, emailguesser/nicknames.py or DEFAULT_DOMAINS in emailguesser/domains.py.
Responsible use
EmailGuesser is meant for OSINT research, authorised security testing, outreach that follows the law, and personal use. Do not use it for harassment, stalking, spam or any unlawful activity. You are responsible for complying with privacy, anti-spam and data-protection laws (such as GDPR and CAN-SPAM) where you operate.
Development and tests
git clone https://github.com/OSINTVerseHQ/EmailGuesser.git
cd EmailGuesser
pip install -e ".[dev]"
ruff check emailguesser tests
ruff format --check emailguesser tests
python -m pytest
emailguesser/
normalize.py name, username and domain cleaning (+ transliteration hook)
nicknames.py bundled nickname table
tokens.py date parsing and name/date tokens
patterns.py pattern table and scores
domains.py default/regional domains and location parsing
known_format.py parse a known-email reference
fields.py input-field aliases
validate.py local-part validation
generator.py core engine and public generate() API
output.py json / jsonl / txt / csv rendering
cli.py command-line interface
tests/ pytest suite + calibration harness
Contributing
Issues and pull requests are welcome. Good first contributions: new patterns for naming conventions you have seen in the wild (use the "new pattern" issue template), nicknames, better scores backed by data, and extra tests. Please run ruff check, ruff format --check and python -m pytest before opening a pull request.
License and attribution
EmailGuesser is released under the MIT License. You are free to use, copy, modify and redistribute it, including commercially, as long as you keep the copyright notice and license text with your copy.
If you use EmailGuesser in a project, report or product, please credit it:
Email candidates generated with EmailGuesser by OSINTverse.
GitHub's Cite this repository button uses CITATION.cff.
Credits
EmailGuesser builds on NAMINT by The SEINT (@SEINT_pl), released under the MIT License. NAMINT's login patterns are the foundation of EmailGuesser's name patterns, and tests/test_generator.py checks that every one of them is still generated. If you want search links for a name rather than email candidates, use NAMINT directly at seintpl.github.io/NAMINT.
The in-browser version runs on Pyodide.
Keywords: email permutator, email generator, email guesser, email finder, email format finder, email pattern generator, email combinations, email permutations, OSINT tool, username to email, name to email, find email by name, Python OSINT, reconnaissance, penetration testing, red team, JSON, CSV.
Metadata
Release files for emailguesser 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| emailguesser-0.4.0.tar.gz | 57.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| emailguesser-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 103.1 kB
Release files / emailguesser-0.4.0.tar.gz
| Download URL | emailguesser-0.4.0.tar.gz |
|---|---|
| Size | 57.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e9b991c0544da5587442b6351ce04714c2d406c87cb45657b26a883cbaaf3313
|
|
BLAKE2b-256 checksum How to use checksums |
3c5bbfc21010a5edf5cbe3fafebbc86defbd2a511f179d7a1d64810206d14861
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / emailguesser-0.4.0-py3-none-any.whl
| Download URL | emailguesser-0.4.0-py3-none-any.whl |
|---|---|
| Size | 45.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
374109ca7fc46577f7ccada3ed954c6a1c6f8bcaa89540af999cceebfb2875b9
|
|
BLAKE2b-256 checksum How to use checksums |
4717b088bc8fe754d70b9a73a95591c4706c57567e679d140abc71ed99ae8949
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency log