OSINT Playbook
Every lead, sourced
and verified.
An automated OSINT pipeline: parallel agents source companies and people across registries, maps, corporate sites and social networks, extract and verify every email and phone, then merge it all into one clean contact database — synced straight to your CRM.
Data sources
Where the leads come from
Business registries
ЕГРЮЛ aggregators — rusprofile.ru, list-org.com, sbis.ru, companies.rbc.ru: registry data, INN/OGRN, founders and executives.
Maps & directories
2GIS (CIS), Yandex Maps (Russia/CIS), Yellow Pages (international) — companies with addresses, phones and opening hours.
Corporate sites
Team and about pages, contacts, press releases — plus Schema.org markup and JSON-LD blocks structured for extraction.
Social networks
LinkedIn profiles and company pages, X/Twitter search, Telegram channels and users — with anti-bot limits handled honestly.
News & mentions
Serper News API and Google News RSS — funding rounds, hires and launches that tell you who to contact next.
Pipeline
Seven stages, one query
Plan
The coordinator decomposes the query into parallel subtasks: registries, socials, corporate sites, databases.
Extract
extract_contacts pulls emails, phones, socials, persons and companies from every fetched page.
Verify
verify_email, verify_phone and verify_social_profile filter the noise and score confidence.
Enrich
enrich_company and enrich_person add industry, size, location, role and buying signals.
ContactDb
SQLite or PostgreSQL store; dedup and merge on normalized emails and phones.
CRM
Sync into amoCRM, Bitrix24 or HubSpot — dedup by crm_id, no duplicates on re-runs.
Report
summary.md plus a contact table, exported to CSV, vCard, JSON or Excel.
Verification
Nothing unverified gets saved
Bounced emails and dead numbers burn outreach budget. Every candidate passes MX checks, disposable-domain and role-based filters before it earns a confidence score — and obfuscated addresses like name [at] domain [dot] com are decoded automatically, with a lower score than plain ones.
verify_email("info@company.com")
→ is_valid_syntax: true
→ domain_exists: true
→ mx_records: ["mx1.company.com"]
→ is_disposable: false
→ is_role_based: true # info@ admin@ support@
→ confidence: 0.85
# deobfuscation
"name [at] company [dot] com"
→ info@company.com # confidence 0.70
"info@company.com" # plain → confidence 0.95Phones & patterns
E.164 phones, inferred emails
Phones are normalized to E.164 via libphonenumber with country and mobile/landline detection. When only a name and a domain are known, suggest_emails builds candidate addresses from name permutations and the domain's existing pattern — up to 9 variants to verify.
verify_phone("+7 (495) 710-75-80")
→ normalized: "+74957107580" # E.164
→ country_code: "RU"
→ is_valid: true
→ is_mobile: false # landline
suggest_emails("John Doe", "acme.com")
→ john.doe@acme.com
→ j.doe@acme.com
→ john@acme.com …
→ up to 9 candidates: name permutations
× observed domain patternStorage
ContactDb — one canonical record
Contacts land in SQLite out of the box — or PostgreSQL for large bases, same interface. Dedup and merge run on normalized emails and phones, so re-runs and overlapping sources never create double records. Tags and notes attach per contact; the whole schema is yours to query.
save_contacts · db_path = "./contacts.db" · pg_url = "postgres://…"
| Table | Columns |
|---|---|
| contacts | id, email, phone, name, title, company, source, timestamps |
| social_profiles | contact_id, platform, url, username |
| companies | name, website, industry, size, location |
| tags | contact_id, tag |
| notes | contact_id, note |
Query examples
One command, a folder of leads
parallel-research run \
"Find contacts of CEOs and CTOs at
small IT companies in Moscow.
Extract emails, phones and
LinkedIn profiles." \
--output ./leads/
# agents fan out over rusprofile,
# list-org and sbis, parse corporate
# sites, enrich persons
# → INN/OGRN table + emails + phones
# + LinkedIn candidates (CSV, md)parallel-research run \
"Find 20 SaaS companies,
Series A–B, Berlin. Collect
emails and LinkedIn of founders
and CTOs." \
--output ./berlin-saas/
# → 20 companies × founder/CTO
# contacts, verified emails +
# LinkedIn URLs, exported to
# CSV / vCardparallel-research run \
"Research the Dubai fintech
startup market. For the top-10
companies find decision-maker
contacts." \
--output ./dubai-fintech/
# → market overview + top-10
# companies with decision-maker
# contacts, CSV / vCard exportEthics & compliance
Responsible by default
GDPR & 152-ФЗ
Personal data is processed in line with GDPR and Russia's 152-ФЗ: lawful basis, scoped purpose, deletion on request.
Public data only
Agents read open, publicly available sources — nothing scraped behind logins, paywalls or private APIs.
robots.txt & rate limits
Crawling respects robots.txt and built-in throttling; bot-blocked pages (403) are skipped gracefully, never hammered.
Honest statuses
Uncertainty is explicit: "requires verification", "not found", "HTTP 999", "low-confidence match" — never silent guesses.
LinkedIn HTTP 999
LinkedIn answers bots with HTTP 999. Stable parsing needs proxies/cookies; agents surface the block instead of inventing data.
No spam
Extracted contacts are for qualified outreach and research — not bulk unsolicited mailings. Confidence scores help you filter.