EIN extractor
Paste a company page, an SEC filing or a Form 990 and pull out every US Employer Identification Number. Prefix-checked, deduplicated and scored by whether a tax-ID label sits next to it.
Nothing yet. Paste content on the left — every nine-digit number in NN-NNNNNNN shape is checked against the IRS prefix list and scored by whether a tax-ID label sits next to it.
Runs entirely in your browser. Nothing you paste is uploaded, stored or logged.
Shape, prefix, label — in that order
An EIN has no check digit, so matching the shape alone is not enough. Three passes get you to a list you can trust.
- Shape: nine digits written as NN-NNNNNNN, with or without the hyphen or a space.
- Prefix: the first two digits must fall in a range the IRS issues — 01-06, 10-16, 20-27, 30-35, 37-39, 41-48, 50-68, 71-77, 80-88, 90-95, 98-99.
- Label: wording such as EIN, FEIN, Federal Tax ID or 'I.R.S. Employer Identification No.' within about 60 characters of the number.
- Everything else — phone numbers, DUNS, order references, zip+4 — is what the label filter is there to remove.
- Duplicates collapse into one row with an occurrence count, and each row keeps the surrounding text so you can sanity-check it.
When you also need to fetch the page
A browser cannot fetch arbitrary URLs. Irmu returns the HTML — rendering, proxies and retries included — and you run the same regex over it.
# pip install requests
# Fetch the page with Irmu — rendering, proxies and retries handled — then run the regex above.
import os, requests
from bs4 import BeautifulSoup
resp = requests.get(
"https://app.irmu.com/api/crawl",
headers={"Authorization": f"Bearer {os.environ['IRMU_API_KEY']}"},
params={"url": "https://example.com/terms", "js": "true"},
timeout=60,
)
resp.raise_for_status()
text = BeautifulSoup(resp.text, "html.parser").get_text(" ", strip=True)
for hit in extract_eins(text, require_label=True):
print(hit["ein"], "—", hit["context"][:80])Do the same thing in your own pipeline
Prefix validation plus label proximity, in four languages.
import re
# Prefixes the IRS actually issues. Everything else is not an EIN.
VALID_PREFIXES = set()
for lo, hi in [(1, 6), (10, 16), (20, 27), (30, 35), (37, 39), (41, 48),
(50, 68), (71, 77), (80, 88), (90, 95), (98, 99)]:
VALID_PREFIXES.update(f"{n:02d}" for n in range(lo, hi + 1))
EIN = re.compile(r"\b(\d{2})[-\s]?(\d{7})\b")
LABEL = re.compile(
r"(ein|fein|employer\s+identification|federal\s+tax\s*id|i\.?r\.?s\.?\s+no)",
re.I,
)
def extract_eins(text: str, require_label: bool = False):
found = {}
for m in EIN.finditer(text):
prefix, rest = m.group(1), m.group(2)
if prefix not in VALID_PREFIXES:
continue
window = text[max(0, m.start() - 60): m.end() + 20]
labelled = bool(LABEL.search(window))
if require_label and not labelled:
continue
ein = f"{prefix}-{rest}"
found.setdefault(ein, {"ein": ein, "labelled": labelled, "context": window.strip()})
return list(found.values())
sample = "Acme Robotics, Inc. Federal Tax ID (EIN): 84-3719205. Order 12-3456789."
print(extract_eins(sample, require_label=True))
# [{'ein': '84-3719205', 'labelled': True, 'context': '...Federal Tax ID (EIN): 84-3719205...'}]Public companies: ask EDGAR, don't scrape
The SEC publishes the EIN of every registrant as structured data. Use it before you crawl anything.
# Public companies print their EIN on the cover page of every filing.
# EDGAR's company facts endpoint returns it directly — no scraping needed.
curl -s -H "User-Agent: your-name your@email.com" \
"https://data.sec.gov/submissions/CIK0000320193.json" \
| jq '{name: .name, ein: .ein, tickers: .tickers}'
# {
# "name": "Apple Inc.",
# "ein": "942404110",
# "tickers": ["AAPL"]
# }Sweeping a list of domains
EINs cluster on a handful of predictable pages. Check those, stop at the first labelled hit, and you spend a few credits per company instead of crawling a whole site.
// Node 18+. Walk a list of company sites, check the pages EINs usually live on,
// and stop as soon as a labelled match is found.
const PATHS = ["/", "/terms", "/privacy", "/about", "/contact", "/imprint", "/legal"];
async function findEin(domain) {
for (const path of PATHS) {
const target = new URL(path, `https://${domain}`).href;
const res = await fetch(
`https://app.irmu.com/api/crawl?url=${encodeURIComponent(target)}&js=true`,
{ headers: { Authorization: `Bearer ${process.env.IRMU_API_KEY}` } },
);
if (!res.ok) continue;
const html = await res.text();
const [hit] = extractEins(html.replace(/<[^>]+>/g, " "), { requireLabel: true });
if (hit) return { domain, ein: hit.ein, source: target };
}
return { domain, ein: null, source: null };
}
for (const domain of ["example.com", "example.org"]) {
console.log(await findEin(domain));
}Questions about EIN extraction
Fetch the pages, extract the fields
One API for rendering, proxies and retries. 200 free credits every month, no card required.