email-enrichment

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Email Enrichment

邮箱信息补全

Two directions, one skill:
  1. Email to person - Turn a list of email addresses into rich contact profiles (name, title, company, profile URL). Uses a 7-phase waterfall optimized for coverage and accuracy.
  2. Person to email - Find business emails, personal emails, and phone numbers for a list of people. Uses enrichment with personal contact info, plus GitHub commit fallbacks for technical people.

两种方向,一个技能:
  1. 邮箱转个人 - 将邮箱地址列表转换为丰富的联系人档案(姓名、职位、公司、档案URL)。采用7阶段瀑布流程,兼顾覆盖范围与准确性。
  2. 个人转邮箱 - 为个人列表查找工作邮箱、个人邮箱及电话号码。通过个人联系方式补全实现,针对技术人员还支持GitHub提交记录作为备选方案。

Overview

概述

The approach uses seven phases in a strict waterfall. Each phase catches emails that earlier phases missed. The phases are ordered by cost (free first, then cheapest) and reliability (highest precision first).
PhaseMCP ToolTargetsCost
1
crustdata_company_identify
Work + Edu emailsFREE
2
crustdata_people_enrich
(
business_email
or
personal_email
) + post-verification
ALL email types (work, edu, personal)Credits
3
crustdata_people_search_db
(name+company)
Missed work/eduCredits
4
crustdata_people_search_db
(emails contains)
ALL remainingCredits
5
crustdata_people_search_db
(name only)
Remaining personalCredits
6
crustdata_web_search
+
crustdata_people_enrich
ALL remainingCredits
7Scoring gateALL candidates from Phases 3-6N/A
Coverage rates:
CategoryPerson MatchCompany Match
Work emails95%+95%+
Edu emails95%+95%+
Personal emails95%+N/A
Blended95%+95%+

本方法采用严格的7阶段瀑布流程,每个阶段处理前序阶段未匹配到的邮箱。阶段按成本(先免费,后低成本)和可靠性(先高精度)排序。
阶段MCP工具目标邮箱类型成本
1
crustdata_company_identify
工作+教育邮箱免费
2
crustdata_people_enrich
business_email
personal_email
)+ 后验证
所有邮箱类型(工作、教育、个人)积分
3
crustdata_people_search_db
(姓名+公司)
未匹配的工作/教育邮箱积分
4
crustdata_people_search_db
(邮箱包含)
所有剩余邮箱积分
5
crustdata_people_search_db
(仅姓名)
剩余个人邮箱积分
6
crustdata_web_search
+
crustdata_people_enrich
所有剩余邮箱积分
7评分校验阶段3-6的所有候选结果
覆盖率:
类别个人匹配率公司匹配率
工作邮箱95%+95%+
教育邮箱95%+95%+
个人邮箱95%+
综合95%+95%+

Phase 0: Parse input and classify emails

阶段0:解析输入并分类邮箱

Read the input

读取输入

Accept CSV files, spreadsheets (.xlsx/.csv), or inline lists. Extract all email addresses. Deduplicate.
支持CSV文件、电子表格(.xlsx/.csv)或内联列表。提取所有邮箱地址并去重。

Classify each email into one of three categories

将每个邮箱分类为以下三类之一

Personal email domains (match against this list):
gmail.com, yahoo.com, hotmail.com, outlook.com, aol.com, icloud.com, me.com,
live.com, protonmail.com, proton.me, msn.com, ymail.com, comcast.net, att.net,
verizon.net, mac.com, fastmail.com, hey.com, pm.me, zoho.com, gmx.com,
googlemail.com
Edu email domains (match against these TLD patterns):
.edu, .ac.uk, .ac.jp, .ac.kr, .ac.in, .ac.nz, .ac.za
Work emails: everything else.
个人邮箱域名(匹配以下列表):
gmail.com, yahoo.com, hotmail.com, outlook.com, aol.com, icloud.com, me.com,
live.com, protonmail.com, proton.me, msn.com, ymail.com, comcast.net, att.net,
verizon.net, mac.com, fastmail.com, hey.com, pm.me, zoho.com, gmx.com,
googlemail.com
教育邮箱域名(匹配以下TLD模式):
.edu, .ac.uk, .ac.jp, .ac.kr, .ac.in, .ac.nz, .ac.za
工作邮箱:其余所有邮箱。

Name extraction from email prefix

从邮箱前缀提取姓名

Split the local part (before
@
) on dots, underscores, and hyphens. Remove any parts that are purely digits. Capitalize each remaining part. Only keep parts with 2+ characters.
python
import re

def extract_name_parts(email):
    local = email.split("@")[0]
    parts = re.split(r'[._\-]', local)
    parts = [p for p in parts if not p.isdigit()]
    parts = [p.capitalize() for p in parts if len(p) >= 2]
    return parts
@
前的本地部分按点、下划线和连字符拆分。移除纯数字部分。将剩余部分首字母大写,仅保留长度≥2的部分。
python
import re

def extract_name_parts(email):
    local = email.split("@")[0]
    parts = re.split(r'[._\-]', local)
    parts = [p for p in parts if not p.isdigit()]
    parts = [p.capitalize() for p in parts if len(p) >= 2]
    return parts

Examples:

示例:

"daniel_k_lee@brown.edu" -> ["Daniel", "Lee"]

"daniel_k_lee@brown.edu" -> ["Daniel", "Lee"]

"john.smith@acme.com" -> ["John", "Smith"]

"john.smith@acme.com" -> ["John", "Smith"]

"jsmith123@gmail.com" -> ["Jsmith"]

"jsmith123@gmail.com" -> ["Jsmith"]

"a.rodriguez@company.com" -> ["Rodriguez"]

"a.rodriguez@company.com" -> ["Rodriguez"]


---

---

Phase 1: Company Identify (FREE)

阶段1:公司识别(免费)

Identify the company behind each non-personal email domain. This phase is FREE and should always run first.
识别非个人邮箱域名对应的公司。此阶段免费,应始终优先执行。

MCP tool call

MCP工具调用

crustdata_company_identify:
  company_website: "domain.com"
crustdata_company_identify:
  company_website: "domain.com"

What it returns

返回内容

Company name, professional network URL, website, description, and other firmographic data. Returned as part of the tool result.
公司名称、职业社交平台URL、官网、描述及其他企业数据,作为工具结果返回。

How to run it

执行方式

Deduplicate domains first. A list of 1,000 work emails might only have 200 unique domains.
For each unique domain extracted from work + edu emails:
crustdata_company_identify:
  company_website: "acme.com"
Store the result in a domain_map:
domain -> company_name
. This will be used in Phase 3 and Phase 4 for verification.
先对域名去重。1000个工作邮箱可能仅对应200个唯一域名。
针对从工作+教育邮箱提取的每个唯一域名:
crustdata_company_identify:
  company_website: "acme.com"
将结果存储在domain_map中:
域名 -> 公司名称
,供阶段3和阶段4验证使用。

Expected results

预期结果

  • 95%+ of work email domains will be identified
  • Edu domains are nearly 100% (universities are well-known)

  • 95%+的工作邮箱域名可被识别
  • 教育域名几乎100%可识别(大学知名度高)

Phase 2: Person Enrich via business_email or personal_email

阶段2:通过business_email或personal_email进行个人信息补全

Look up each email directly using person enrichment. Phase 2 branches based on email type:
  • Work/edu emails -> use
    business_email
    parameter + post-verification
  • Personal emails -> use
    personal_email
    parameter, no post-verification needed
直接通过个人信息补全工具查询每个邮箱。阶段2根据邮箱类型分支处理:
  • 工作/教育邮箱 -> 使用
    business_email
    参数 + 后验证
  • 个人邮箱 -> 使用
    personal_email
    参数,无需后验证

Branch A: Work/edu emails (business_email)

分支A:工作/教育邮箱(business_email)

MCP tool call

MCP工具调用

crustdata_people_enrich:
  business_email: "john@acme.com"
  fields: "name,business_email"
crustdata_people_enrich:
  business_email: "john@acme.com"
  fields: "name,business_email"

Critical details

关键细节

  • business_email
    takes a single email string
  • linkedin_profile_url
    and
    business_email
    are mutually exclusive -- you cannot use both in the same call
  • Despite the name "business_email", this works for edu emails too (especially faculty/staff)
  • Returns person data including: name, headline, profile URL, current and past employers
  • business_email
    接受单个邮箱字符串
  • linkedin_profile_url
    business_email
    互斥——不能在同一调用中同时使用
  • 尽管名为
    business_email
    ,此参数也适用于教育邮箱(尤其针对教职员工)
  • 返回个人数据包括:姓名、职位头衔、档案URL、现任及过往雇主

How to run it

执行方式

For each work + edu email:
crustdata_people_enrich:
  business_email: "stephen@spero.vc"
  fields: "name,business_email"
If a match is returned (has a
name
field), it MUST pass post-verification before accepting (see Post-verification section below).
针对每个工作+教育邮箱:
crustdata_people_enrich:
  business_email: "stephen@spero.vc"
  fields: "name,business_email"
如果返回匹配结果(包含
name
字段),必须通过后验证才能接受(见下文后验证部分)。

Branch B: Personal emails (personal_email)

分支B:个人邮箱(personal_email)

MCP tool call

MCP工具调用

crustdata_people_enrich:
  personal_email: "bert.zacharin@gmail.com"
  enrich_realtime: true
crustdata_people_enrich:
  personal_email: "bert.zacharin@gmail.com"
  enrich_realtime: true

Critical details

关键细节

  • personal_email
    accepts personal email domains only (gmail, yahoo, outlook, etc.). Business/work emails will be rejected by the API.
  • personal_email
    is mutually exclusive with
    linkedin_profile_url
    ,
    business_email
    , and
    github_profile_url
    -- you cannot combine it with any other identifier in the same call.
  • Cost: 3 credits per lookup, 5 credits with
    enrich_realtime: true
  • No post-verification needed. The API performs its own matching for personal emails. If it returns a result, accept it directly.
  • Access-controlled feature. This parameter needs to be enabled on the account. If the call fails or returns empty, fall through to Phases 4/5/6.
  • personal_email
    仅接受个人邮箱域名(gmail、yahoo、outlook等)。工作/商务邮箱会被API拒绝。
  • personal_email
    linkedin_profile_url
    business_email
    github_profile_url
    互斥——不能在同一调用中与其他标识符组合使用。
  • 成本:每次查询3积分,启用
    enrich_realtime: true
    则为5积分
  • 无需后验证。API会自行对个人邮箱进行匹配验证,若返回结果则直接接受。
  • 权限控制功能:此参数需要在账户中启用。若调用失败或返回空结果,则进入阶段4/5/6作为备选方案。

How to run it

执行方式

For each personal email:
crustdata_people_enrich:
  personal_email: "joanne.bradford@gmail.com"
  enrich_realtime: true
If a match is returned (has a
name
field), accept it directly -- no post-verification required.
If no match is returned, the email falls through to Phase 4/5/6 as fallbacks.
Note: The
personal_email
parameter (released April 2026) is now the primary approach for personal emails. Phases 4 and 5 serve as fallbacks for what Phase 2 misses.
针对每个个人邮箱:
crustdata_people_enrich:
  personal_email: "joanne.bradford@gmail.com"
  enrich_realtime: true
如果返回匹配结果(包含
name
字段),直接接受——无需后验证。
若无匹配结果,邮箱进入阶段4/5/6作为备选方案。
注意
personal_email
参数(2026年4月发布)现已成为处理个人邮箱的主要方式。阶段4和5仅作为阶段2未匹配到的备选方案。

Post-verification (required for every Phase 2 Branch A result -- work/edu only)

后验证(阶段2分支A结果必填——仅针对工作/教育邮箱)

The person enrich API can return wrong matches: a person at the right company but not the email owner, or a person who no longer works there. Every result must pass these checks:
Check 1: Employer domain verification. The email domain must appear in the person's current OR past employer website domains. For example,
dave@sapphireventures.com
must have
sapphireventures.com
in at least one employer's domain list. If the domain doesn't appear in any employer (current or past), REJECT the match.
python
def verify_employer_domain(profile, email_domain):
    domain_base = email_domain.lower().split('.')[0]
    for emp in profile.get("current_employers", []) + profile.get("past_employers", []):
        for d in emp.get("employer_company_website_domain", []):
            if email_domain.lower() in d.lower() or d.lower() in email_domain.lower():
                return True, emp.get("employer_name", "")
        if len(domain_base) > 3 and domain_base in emp.get("employer_name", "").lower().replace(" ", ""):
            return True, emp.get("employer_name", "")
    return False, None
Check 2: Name-prefix match. The email prefix must plausibly match the returned person's name. For example,
talling@lamppostgroup.com
should match a name containing "alling" (as in "Ted Alling"), not "Santosh Sankar". Check if any part of the profile name starts with the same characters as the email prefix, or if a first-initial + lastname pattern matches.
python
def name_matches_prefix(profile_name, email_prefix):
    pn_parts = profile_name.lower().split()
    prefix = email_prefix.lower()
    for part in pn_parts:
        if prefix.startswith(part[:3]) or part.startswith(prefix[:3]):
            return True
        # First-initial + lastname pattern: "talling" = "t" + "alling"
        if len(prefix) > 2:
            for i in range(1, min(3, len(prefix))):
                if prefix[i:] in part and len(prefix[i:]) > 2:
                    return True
    return False
Check 3: AI correction for name mismatches. When the employer domain matches but the name doesn't (right company, wrong person), use web search AI mode to find who actually owns the email:
crustdata_web_search:
  query: "who is talling@lamppostgroup.com"
  sources: ["ai"]
The AI response typically says something like "belongs to Ted Alling, Partner at Lamp Post Group". Extract the real name and search PersonDB with the corrected name + company.
This step recovered 7 correct matches in testing that would otherwise have been lost.
个人信息补全API可能返回错误匹配:同一公司的其他员工,或已离职人员。所有结果必须通过以下检查:
检查1:雇主域名验证。邮箱域名必须出现在个人的现任或过往雇主官网域名中。例如,
dave@sapphireventures.com
必须在至少一个雇主的域名列表中包含
sapphireventures.com
。若域名未出现在任何雇主(现任或过往)中,拒绝匹配结果。
python
def verify_employer_domain(profile, email_domain):
    domain_base = email_domain.lower().split('.')[0]
    for emp in profile.get("current_employers", []) + profile.get("past_employers", []):
        for d in emp.get("employer_company_website_domain", []):
            if email_domain.lower() in d.lower() or d.lower() in email_domain.lower():
                return True, emp.get("employer_name", "")
        if len(domain_base) > 3 and domain_base in emp.get("employer_name", "").lower().replace(" ", ""):
            return True, emp.get("employer_name", "")
    return False, None
检查2:姓名前缀匹配。邮箱前缀必须与返回的个人姓名合理匹配。例如,
talling@lamppostgroup.com
应匹配包含“alling”的姓名(如“Ted Alling”),而非“Santosh Sankar”。检查档案姓名的任意部分是否与邮箱前缀开头字符一致,或是否符合“首字母+姓氏”模式。
python
def name_matches_prefix(profile_name, email_prefix):
    pn_parts = profile_name.lower().split()
    prefix = email_prefix.lower()
    for part in pn_parts:
        if prefix.startswith(part[:3]) or part.startswith(prefix[:3]):
            return True
        # 首字母+姓氏模式:"talling" = "t" + "alling"
        if len(prefix) > 2:
            for i in range(1, min(3, len(prefix))):
                if prefix[i:] in part and len(prefix[i:]) > 2:
                    return True
    return False
检查3:姓名不匹配时的AI修正。当雇主域名匹配但姓名不匹配(公司正确,人员错误)时,使用网页搜索AI模式查找邮箱实际所有者:
crustdata_web_search:
  query: "who is talling@lamppostgroup.com"
  sources: ["ai"]
AI响应通常会显示类似“属于Lamp Post Group合伙人Ted Alling”的内容。提取真实姓名并结合公司信息在PersonDB中搜索。
测试中,此步骤找回了7个原本会丢失的正确匹配结果。

Expected results

预期结果

  • Work/edu (Branch A): ~58% of work+edu emails pass all verification checks. ~5% are rejected by employer domain check (wrong person entirely). ~1% are AI-corrected (right company, wrong person -> AI finds the real name).
  • Personal (Branch B): ~90% of personal emails resolve directly via
    personal_email
    parameter. No post-verification needed.

  • 工作/教育邮箱(分支A):约58%的工作+教育邮箱通过所有验证检查。约5%被雇主域名检查拒绝(完全错误的人员)。约1%通过AI修正(公司正确,人员错误→AI找到真实姓名)。
  • 个人邮箱(分支B):约90%的个人邮箱通过
    personal_email
    参数直接匹配,无需后验证。

Phase 3: PersonDB name+company search

阶段3:PersonDB姓名+公司搜索

For work/edu emails that Phase 2 missed, try a name+company search. Extract a name guess from the email prefix and combine it with the company identified in Phase 1.
针对阶段2未匹配到的工作/教育邮箱,尝试姓名+公司搜索。从邮箱前缀提取姓名猜测,结合阶段1识别的公司信息。

When to use

使用场景

Only for emails where:
  1. Phase 2 returned no match
  2. The email is work or edu (not personal)
  3. The domain was identified in Phase 1 (we know the company name)
  4. At least one name part can be extracted from the email prefix
仅适用于以下情况的邮箱:
  1. 阶段2未返回匹配结果
  2. 邮箱为工作或教育类型(非个人)
  3. 域名已在阶段1识别(已知公司名称)
  4. 可从邮箱前缀提取至少一个姓名部分

MCP tool call

MCP工具调用

crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "FirstName"
      - filter_type: "current_employers.name"
        type: "(.)"
        value: "CompanyName"
  page_size: 3
Note: use
filter_type
as the key for
name
and
current_employers.name
fields.
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "FirstName"
      - filter_type: "current_employers.name"
        type: "(.)"
        value: "CompanyName"
  page_size: 3
注意:
name
current_employers.name
字段需使用
filter_type
作为键。

Verification (required)

验证(必填)

The returned profile's name must contain the first name extracted from the email prefix:
python
def verify_name_match(email_name_parts, profile_name):
    if not email_name_parts or not profile_name:
        return False
    return email_name_parts[0].lower() in profile_name.lower()
返回的档案姓名必须包含从邮箱前缀提取的名字:
python
def verify_name_match(email_name_parts, profile_name):
    if not email_name_parts or not profile_name:
        return False
    return email_name_parts[0].lower() in profile_name.lower()

How to run it

执行方式

For each missed work/edu email, extract the name and look up the company from the domain_map:
undefined
针对每个未匹配的工作/教育邮箱,提取姓名并从domain_map中查找公司信息:
undefined

For email: kyle@backswingventures.com

邮箱:kyle@backswingventures.com

Name parts: ["Kyle"]

姓名部分:["Kyle"]

Company from Phase 1: "Backswing Ventures"

阶段1获取的公司:"Backswing Ventures"

crustdata_people_search_db: filters: op: "and" conditions: - filter_type: "name" type: "(.)" value: "Kyle" - filter_type: "current_employers.name" type: "(.)" value: "Backswing Ventures" page_size: 3

Check each returned profile: does "kyle" appear in the profile's name? If yes, it's a match.
crustdata_people_search_db: filters: op: "and" conditions: - filter_type: "name" type: "(.)" value: "Kyle" - filter_type: "current_employers.name" type: "(.)" value: "Backswing Ventures" page_size: 3

检查每个返回的档案:“kyle”是否出现在档案姓名中?若是则为匹配结果。

Expected results

预期结果

  • Catches emails that Phase 2 missed using name + company compound search
  • Works best for emails with clear name formats (john.smith@, daniel_lee@)

  • 通过姓名+公司组合搜索,捕获阶段2未匹配到的邮箱
  • 对格式清晰的邮箱(如john.smith@、daniel_lee@)效果最佳

Phase 4: PersonDB email contains search + verification

阶段4:PersonDB邮箱包含搜索+验证

Search PersonDB's
emails
field, which contains personal and alternative email addresses stored in profiles. This works for ALL email types: work, edu, and personal.
搜索PersonDB的
emails
字段,该字段包含档案中存储的个人邮箱及备用邮箱地址。适用于所有邮箱类型:工作、教育、个人。

Critical implementation details

关键实现细节

  1. The
    emails
    field in PersonDB is an array field containing personal emails
  2. You MUST use
    "column"
    as the key (not
    "filter_type"
    ) -- this is a PersonDB-specific requirement for this field
  3. Search on the local part only (before the @) to catch cases where the domain might differ
  4. This is the highest false-positive phase -- verification is essential
  1. PersonDB中的
    emails
    字段是数组字段,包含个人邮箱
  2. 必须使用
    "column"
    作为键(而非
    "filter_type"
    )——这是PersonDB针对该字段的特定要求
  3. 仅搜索本地部分
    @
    之前的内容),以捕获域名不同的情况
  4. 此阶段假阳性率最高——验证至关重要

MCP tool call

MCP工具调用

crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - column: "emails"
        type: "(.)"
        value: "LOCAL_PART_OF_EMAIL"
  page_size: 5
IMPORTANT: Use
column
here, NOT
filter_type
. The
emails
field requires the
column
key. Using
filter_type
will silently return zero results.
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - column: "emails"
        type: "(.)"
        value: "LOCAL_PART_OF_EMAIL"
  page_size: 5
重要提示:此处使用
column
,而非
filter_type
emails
字段需要
column
键,使用
filter_type
会静默返回零结果。

How to run it

执行方式

For each remaining unmatched email (work, edu, AND personal):
undefined
针对每个剩余未匹配邮箱(工作、教育、个人):
undefined

For email: joanne.bradford@gmail.com

邮箱:joanne.bradford@gmail.com

Local part: "joanne.bradford"

本地部分:"joanne.bradford"

crustdata_people_search_db: filters: op: "and" conditions: - column: "emails" type: "(.)" value: "joanne.bradford" page_size: 5
undefined
crustdata_people_search_db: filters: op: "and" conditions: - column: "emails" type: "(.)" value: "joanne.bradford" page_size: 5
undefined

Verification logic (CRITICAL -- do not skip)

验证逻辑(至关重要——不可跳过)

Without verification, contains searches produce false positives. For example, searching for "wraecca" might match "Alessandro Racca" because "racca" is a substring.
For every result, verify ALL of the following:
Step 1 -- Name verification (required for all email types):
  • Extract name parts from the email prefix
  • If 2+ name parts: BOTH first AND last must appear in the profile name
  • If 1 name part: that part must appear in the profile name, and the part must be 3+ characters
Step 2 -- Organization verification (required for work and edu emails):
  • Look up the company/institution from the domain_map (Phase 1)
  • Extract significant words from the org name (skip common words like "the", "inc", "llc", "of")
  • At least one significant org word must appear somewhere in the profile data (check employers, education, headline)
  • If no org word matches, reject the result even if the name matched
Step 3 -- Personal emails (name check only):
  • For personal emails (gmail, yahoo, etc.), there is no org to cross-reference
  • The name match from Step 1 is the only gate
  • This means personal email matches have lower precision
python
def verify_phase4_match(email, name_parts, profile, domain_map):
    profile_name = profile.get("name", "").lower()

    # Name verification
    if len(name_parts) >= 2:
        if not (name_parts[0].lower() in profile_name and name_parts[-1].lower() in profile_name):
            return False
    elif len(name_parts) == 1 and len(name_parts[0]) > 2:
        if name_parts[0].lower() not in profile_name:
            return False
    else:
        return False

    # Org verification for work/edu
    domain = email.split("@")[1]
    company_info = domain_map.get(domain)
    if company_info:
        org_name = company_info.get("name", "")
        skip = {"the", "inc", "llc", "ltd", "co", "corp", "of", "and", "for", "university", "college"}
        org_words = [w.lower() for w in org_name.split() if w.lower() not in skip and len(w) > 2]
        profile_str = str(profile).lower()
        if org_words and not any(w in profile_str for w in org_words):
            return False

    return True
若无验证,包含搜索会产生大量假阳性结果。例如,搜索“wraecca”可能匹配“Alessandro Racca”,因为“racca”是其子串。
针对每个结果,必须验证以下所有项:
步骤1——姓名验证(所有邮箱类型必填):
  • 从邮箱前缀提取姓名部分
  • 若有2个及以上姓名部分:名字和姓氏必须同时出现在档案姓名中
  • 若只有1个姓名部分:该部分必须出现在档案姓名中,且长度≥3个字符
步骤2——组织验证(工作和教育邮箱必填):
  • 从domain_map(阶段1)中查找公司/机构信息
  • 从组织名称中提取关键词汇(跳过常见词汇如“the”“inc”“llc”“of”)
  • 至少一个关键组织词汇必须出现在档案数据中(检查雇主、教育经历、职位头衔)
  • 若无组织词汇匹配,即使姓名匹配也需拒绝结果
步骤3——个人邮箱(仅姓名检查):
  • 针对个人邮箱(gmail、yahoo等),无组织可交叉验证
  • 仅需通过步骤1的姓名匹配检查
  • 这意味着个人邮箱匹配的精度较低
python
def verify_phase4_match(email, name_parts, profile, domain_map):
    profile_name = profile.get("name", "").lower()

    # 姓名验证
    if len(name_parts) >= 2:
        if not (name_parts[0].lower() in profile_name and name_parts[-1].lower() in profile_name):
            return False
    elif len(name_parts) == 1 and len(name_parts[0]) > 2:
        if name_parts[0].lower() not in profile_name:
            return False
    else:
        return False

    # 工作/教育邮箱的组织验证
    domain = email.split("@")[1]
    company_info = domain_map.get(domain)
    if company_info:
        org_name = company_info.get("name", "")
        skip = {"the", "inc", "llc", "ltd", "co", "corp", "of", "and", "for", "university", "college"}
        org_words = [w.lower() for w in org_name.split() if w.lower() not in skip and len(w) > 2]
        profile_str = str(profile).lower()
        if org_words and not any(w in profile_str for w in org_words):
            return False

    return True

Expected results

预期结果

  • With strict verification, this phase adds ~3% more matches
  • Without verification, the false positive rate is extremely high (in testing: 1,540 rejections vs 37 accepts)
  • For work/edu emails: both name AND company must verify. No company from Phase 1 = automatic reject.
  • For personal emails: name match only (lower precision, but better than no verification)

  • 严格验证后,此阶段可新增约3%的匹配结果
  • 无验证时假阳性率极高(测试中:1540个拒绝结果 vs 37个接受结果)
  • 工作/教育邮箱:必须同时通过姓名和公司验证。阶段1未识别到公司则自动拒绝。
  • 个人邮箱:仅需姓名匹配(精度较低,但比无验证好)

Phase 5: PersonDB name search for personal emails

阶段5:针对个人邮箱的PersonDB姓名搜索

Last resort for personal emails where we can extract a plausible full name from the email prefix.
从邮箱前缀可提取合理全名的个人邮箱的最终备选方案。

When to use

使用场景

Only for emails where:
  1. All previous phases returned no match
  2. The email is personal (gmail, yahoo, etc.)
  3. At least 2 name parts can be extracted from the email prefix
仅适用于以下情况的邮箱:
  1. 所有前序阶段均未返回匹配结果
  2. 邮箱为个人类型(gmail、yahoo等)
  3. 可从邮箱前缀提取至少2个姓名部分

MCP tool call

MCP工具调用

crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "FirstName LastName"
  page_size: 5
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "FirstName LastName"
  page_size: 5

Acceptance criteria

接受标准

  • The search must return 3 or fewer results (low ambiguity)
  • If 4+ results come back, skip -- too many possible matches
  • The returned name must reasonably match the extracted name parts
  • 搜索必须返回3个及以下结果(低歧义)
  • 若返回4个及以上结果则跳过——可能匹配的人员过多
  • 返回的姓名必须与提取的姓名部分合理匹配

How to run it

执行方式

undefined
undefined

For email: joanne.bradford@gmail.com

邮箱:joanne.bradford@gmail.com

Name parts: ["Joanne", "Bradford"]

姓名部分:["Joanne", "Bradford"]

crustdata_people_search_db: filters: op: "and" conditions: - filter_type: "name" type: "(.)" value: "Joanne Bradford" page_size: 5

If 1-3 results returned, take the first one. If 0 or 4+, mark as unmatched.
crustdata_people_search_db: filters: op: "and" conditions: - filter_type: "name" type: "(.)" value: "Joanne Bradford" page_size: 5

若返回1-3个结果,取第一个。若返回0或4个及以上,标记为未匹配。

Expected results

预期结果

  • Catches remaining personal emails with clear first.last patterns
  • The 3-result ceiling prevents matching the wrong person for common names
  • MUST verify: both name parts from the email prefix must appear in the returned profile name. Do not just accept the first result.

  • 捕获剩余的格式清晰的个人邮箱(如first.last模式)
  • 3个结果的上限避免了常见姓名匹配错误人员的情况
  • 必须验证:从邮箱前缀提取的两个姓名部分必须出现在返回的档案姓名中,不可直接接受第一个结果

Phase 6: Web search fallback for all remaining unmatched emails

阶段6:所有剩余未匹配邮箱的网页搜索备选方案

Final fallback for emails that all previous phases missed. Uses web search to find the person's profile URL, then enriches via that URL. This catches vanity domains (e.g., carolewainaina.com), personal brand domains, and any email not indexed in Crustdata's database.
针对所有前序阶段未匹配到的邮箱的最终备选方案。通过网页搜索查找个人档案URL,再通过该URL进行信息补全。可捕获 vanity域名(如carolewainaina.com)、个人品牌域名及未被Crustdata数据库收录的邮箱。

When to use

使用场景

For any email that remains unmatched after Phases 1-5, regardless of category (work, edu, or personal).
适用于阶段1-5后仍未匹配的所有邮箱,无论类型(工作、教育、个人)。

Step 1: Web search for profile URL

步骤1:网页搜索档案URL

crustdata_web_search:
  query: "EMAIL linkedin"
  sources: ["web"]
Check the results for any URL containing
linkedin.com/in/
. If found, proceed to Step 3.
crustdata_web_search:
  query: "EMAIL linkedin"
  sources: ["web"]
检查结果中是否包含
linkedin.com/in/
的URL。若找到,进入步骤3。

Step 2: AI web search for name (if Step 1 didn't find a profile URL)

步骤2:AI网页搜索姓名(若步骤1未找到档案URL)

crustdata_web_search:
  query: "who is EMAIL"
  sources: ["ai"]
The AI response often says something like "belongs to Carole Wamuyu Wainaina" or "associated with John Smith at Company X". Extract the person's name and search PersonDB:
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "Extracted Name"
  page_size: 3
If PersonDB returns a result with a profile URL, proceed to Step 3.
crustdata_web_search:
  query: "who is EMAIL"
  sources: ["ai"]
AI响应通常会显示类似“属于Carole Wamuyu Wainaina”或“与Company X的John Smith相关”的内容。提取个人姓名并在PersonDB中搜索:
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "Extracted Name"
  page_size: 3
若PersonDB返回包含档案URL的结果,进入步骤3。

Step 3: Enrich via profile URL

步骤3:通过档案URL进行信息补全

crustdata_people_enrich:
  linkedin_profile_url: "PROFILE_URL_FROM_STEP_1_OR_2"
  fields: "name,business_email"
crustdata_people_enrich:
  linkedin_profile_url: "PROFILE_URL_FROM_STEP_1_OR_2"
  fields: "name,business_email"

Rate limit note

速率限制说明

Web search is rate-limited at 10 RPM (6 seconds between calls). This phase is slow by design. Only run it on emails that all other phases missed.
网页搜索的速率限制为10 RPM(调用间隔6秒)。此阶段设计为慢速执行,仅针对所有其他阶段未匹配到的邮箱运行。

Expected results

预期结果

  • Catches vanity/personal domains (carolewainaina.com, first-last.com)
  • Catches people not indexed by email but findable via web search
  • The AI mode is particularly effective at resolving "who owns this email" queries

  • 捕获vanity/个人域名(carolewainaina.com、first-last.com)
  • 捕获未通过邮箱收录但可通过网页搜索找到的人员
  • AI模式在解决“谁拥有此邮箱”的查询时效果尤为显著

Phase 7: Scoring gate (applied to all candidates from Phases 3-6)

阶段7:评分校验(应用于阶段3-6的所有候选结果)

All candidate matches produced by Phases 3, 4, 5, and 6 must pass through this scoring gate before being accepted. Phase 2 results are exempt (Phase 2 Branch A has its own post-verification; Phase 2 Branch B results from
personal_email
are pre-verified by the API).
阶段3、4、5、6产生的所有候选匹配结果必须通过此评分校验才能被接受。阶段2结果除外(阶段2分支A有自己的后验证;阶段2分支B通过
personal_email
返回的结果已由API预验证)。

Hard requirements (both must pass)

硬性要求(必须同时满足)

  1. name_sim > 0.8 -- The similarity between the name extracted from the email prefix and the candidate profile name must exceed 0.8. This prevents a perfect company match from compensating for a bad name match.
  2. combined_score > 0.7 -- The overall combined score (incorporating name similarity, company match, and any other signals) must exceed 0.7.
Phase 7 requires BOTH
name_sim > 0.8
AND
combined_score > 0.7
. This prevents a perfect company match from compensating for a bad name match. For example, finding someone at the right company whose name does not resemble the email prefix will be rejected even if the company match is perfect.
  1. name_sim > 0.8——从邮箱前缀提取的姓名与候选档案姓名的相似度必须超过0.8。避免因公司匹配完美而忽略姓名匹配不佳的情况。
  2. combined_score > 0.7——综合得分(包含姓名相似度、公司匹配及其他信号)必须超过0.7。
阶段7要求同时满足name_sim > 0.8和combined_score > 0.7。避免因公司匹配完美而忽略姓名匹配不佳的情况。例如,找到同一公司但姓名与邮箱前缀不相似的人员,即使公司匹配完美也会被拒绝。

When a candidate fails

候选结果未通过时的处理

If a candidate fails the scoring gate, it is rejected and the email continues to the next phase in the waterfall. If no phase produces a candidate that passes the gate, the email is marked UNMATCHED.

若候选结果未通过评分校验,则被拒绝,邮箱进入瀑布流程的下一阶段。若所有阶段均未产生通过校验的候选结果,邮箱标记为未匹配。

Rate limiting

速率限制

Crustdata uses a leaky bucket algorithm. Requests must be distributed evenly -- bursting will trigger 429 errors even if you are under the per-minute limit.
Default rate limits:
MCP ToolEndpointRPMMin Interval
crustdata_company_identify
/screener/identify
302 seconds
crustdata_people_enrich
/screener/person/enrich
154 seconds
crustdata_people_search_db
/screener/persondb/search
601 second
crustdata_web_search
/screener/web-search
106 seconds
When processing large lists, space out MCP tool calls accordingly. If you get rate-limited (429 error in the tool response), back off with exponential delays: wait 2s, then 4s, then 8s before retrying.
Crustdata采用漏桶算法。请求必须均匀分布——即使未达到每分钟限制,突发请求也会触发429错误。
默认速率限制:
MCP工具端点RPM最小间隔
crustdata_company_identify
/screener/identify
302秒
crustdata_people_enrich
/screener/person/enrich
154秒
crustdata_people_search_db
/screener/persondb/search
601秒
crustdata_web_search
/screener/web-search
106秒
处理大型列表时,需相应间隔MCP工具调用。若遇到速率限制(工具响应中出现429错误),使用指数退避策略重试:先等待2秒,再4秒,再8秒。

Optimization: deduplicate domains in Phase 1

优化:阶段1中对域名去重

A list of 1,000 work emails might only have 200 unique domains. Always deduplicate domains before calling
crustdata_company_identify
.

1000个工作邮箱可能仅对应200个唯一域名。调用
crustdata_company_identify
前务必对域名去重。

Progress saving and resumability

进度保存与续跑

For large lists, save progress to a JSON file after each phase so the enrichment can resume if interrupted.
针对大型列表,每个阶段完成后将进度保存到JSON文件,以便中断后可继续信息补全。

Progress file format

进度文件格式

json
{
  "phase_completed": 3,
  "domain_map": {
    "acme.com": {"name": "Acme Corp"},
    "stanford.edu": {"name": "Stanford University"}
  },
  "results": {
    "john@acme.com": {
      "name": "John Smith",
      "headline": "VP Engineering at Acme",
      "company": "Acme Corp",
      "method": "person_enrich"
    }
  },
  "unmatched_emails": ["unknown@gmail.com"]
}
json
{
  "phase_completed": 3,
  "domain_map": {
    "acme.com": {"name": "Acme Corp"},
    "stanford.edu": {"name": "Stanford University"}
  },
  "results": {
    "john@acme.com": {
      "name": "John Smith",
      "headline": "VP Engineering at Acme",
      "company": "Acme Corp",
      "method": "person_enrich"
    }
  },
  "unmatched_emails": ["unknown@gmail.com"]
}

Resume logic

续跑逻辑

On start, check if a progress file exists. If it does, skip phases that are already complete and continue from where it left off.

启动时检查是否存在进度文件。若存在,跳过已完成的阶段,从上次中断处继续。

Output

输出

CSV output

CSV输出

Generate a CSV with these columns:
ColumnDescription
email
Original email address
category
work
,
edu
, or
personal
person_name
Full name of the person
person_headline
Job title / headline
company_name
Company or institution name
profile_url
Professional profile URL
method
Which phase found the match:
person_enrich
,
name+company
,
email_contains
,
name_search
生成包含以下列的CSV:
列名描述
email
原始邮箱地址
category
work
edu
personal
person_name
个人全名
person_headline
职位头衔
company_name
公司或机构名称
profile_url
职业档案URL
method
匹配所在阶段:
person_enrich
name+company
email_contains
name_search

Summary statistics

统计摘要

Print a summary at the end:
=== Email Enrichment Results ===
Total emails: {N}
  Work:     {W}  | Person: {P1} ({P1%})  | Company: {C1} ({C1%})
  Edu:      {E}  | Person: {P2} ({P2%})  | Company: {C2} ({C2%})
  Personal: {R}  | Person: {P3} ({P3%})  | Company: {C3} ({C3%})
  Overall:  {N}  | Person: {PT} ({PT%})  | Company: {CT} ({CT%})

Breakdown by method:
  Phase 2 (person_enrich):    {count}
  Phase 3 (name+company):     {count}
  Phase 4 (email_contains):   {count}
  Phase 5 (name_search):      {count}

结束时打印摘要:
=== 邮箱信息补全结果 ===
总邮箱数: {N}
  工作邮箱:     {W}  | 匹配个人: {P1} ({P1%})  | 匹配公司: {C1} ({C1%})
  教育邮箱:      {E}  | 匹配个人: {P2} ({P2%})  | 匹配公司: {C2} ({C2%})
  个人邮箱: {R}  | 匹配个人: {P3} ({P3%})  | 匹配公司: {C3} ({C3%})
  综合:  {N}  | 匹配个人: {PT} ({PT%})  | 匹配公司: {CT} ({CT%})

按方法细分:
  阶段2 (person_enrich):    {count}
  阶段3 (name+company):     {count}
  阶段4 (email_contains):   {count}
  阶段5 (name_search):      {count}

Decision flowchart

决策流程图

For each email:
|
+-- Classify: work / edu / personal
|
+-- Phase 1: Is it work or edu?
|   +-- Yes -> Extract domain -> crustdata_company_identify(company_website=domain)
|   |   +-- Found company? -> Store in domain_map
|   |   +-- Not found? -> Continue (no company info for this domain)
|   +-- No (personal) -> Skip to Phase 2 Branch B
|
+-- Phase 2: Branch by email type
|   |
|   +-- Work/edu (Branch A):
|   |   +-- crustdata_people_enrich(business_email=email, fields="name,business_email")
|   |   +-- Found person? -> Post-verify (employer domain + name prefix + AI correction)
|   |   |   +-- Verified? -> DONE (method=person_enrich)
|   |   |   +-- Failed verification? -> Continue to Phase 3
|   |   +-- Not found? -> Continue to Phase 3
|   |
|   +-- Personal (Branch B):
|       +-- crustdata_people_enrich(personal_email=email, enrich_realtime=true)
|       +-- Found person? -> DONE (method=person_enrich_personal) -- no post-verification needed
|       +-- Not found? -> Continue to Phase 4 (skip Phase 3)
|
+-- Phase 3: Is it work/edu AND have company name AND name parts?
|   +-- Yes -> crustdata_people_search_db(filters: name + current_employers.name)
|   |   +-- Found + name verified? -> Phase 7 scoring gate -> DONE (method=name+company)
|   |   +-- Not found? -> Continue to Phase 4
|   +-- No -> Skip to Phase 4
|
+-- Phase 4: Still unmatched? (any email type)
|   +-- crustdata_people_search_db(filters: column="emails", type="(.)", value=local_part)
|   +-- For each result, verify:
|   |   +-- Name parts match profile name? (required)
|   |   +-- Work/edu: org appears in profile history? (required)
|   |   +-- Personal: name match only (no org check possible)
|   +-- Verified match? -> Phase 7 scoring gate -> DONE (method=email_contains)
|   +-- No verified match? -> Continue to Phase 5
|
+-- Phase 5: Is it personal AND has 2+ name parts?
|   +-- Yes -> crustdata_people_search_db(filters: name="FirstName LastName")
|   |   +-- 1-3 results returned + name verified? -> Phase 7 scoring gate -> DONE (method=name_search)
|   |   +-- 0 or 4+ results? -> Continue to Phase 6
|   +-- No -> Continue to Phase 6
|
+-- Phase 6: Still unmatched? (any email type)
|   +-- crustdata_web_search(query="EMAIL linkedin", sources=["web"])
|   |   +-- Found linkedin.com/in/ URL? -> crustdata_people_enrich(linkedin_profile_url=URL) -> Phase 7 scoring gate -> DONE
|   +-- No URL found? -> crustdata_web_search(query="who is EMAIL", sources=["ai"])
|   |   +-- Extracted person name? -> crustdata_people_search_db(name) -> get profile URL -> enrich -> Phase 7 scoring gate -> DONE
|   +-- Nothing found? -> UNMATCHED
|
+-- Phase 7: Scoring gate (applied to all candidates from Phases 3-6)
    +-- Requires BOTH: name_sim > 0.8 AND combined_score > 0.7
    +-- Pass? -> Accept match
    +-- Fail? -> Reject, continue to next phase or mark UNMATCHED

针对每个邮箱:
|
+-- 分类: 工作 / 教育 / 个人
|
+-- 阶段1: 是否为工作或教育邮箱?
|   +-- 是 -> 提取域名 -> crustdata_company_identify(company_website=域名)
|   |   +-- 找到公司? -> 存储到domain_map
|   |   +-- 未找到? -> 继续(该域名无公司信息)
|   +-- 否(个人) -> 跳至阶段2分支B
|
+-- 阶段2: 按邮箱类型分支
|   |
|   +-- 工作/教育邮箱(分支A):
|   |   +-- crustdata_people_enrich(business_email=邮箱, fields="name,business_email")
|   |   +-- 找到个人? -> 后验证(雇主域名 + 姓名前缀 + AI修正)
|   |   |   +-- 验证通过? -> 完成(method=person_enrich)
|   |   |   +-- 验证失败? -> 进入阶段3
|   |   +-- 未找到? -> 进入阶段3
|   |
|   +-- 个人邮箱(分支B):
|       +-- crustdata_people_enrich(personal_email=邮箱, enrich_realtime=true)
|       +-- 找到个人? -> 完成(method=person_enrich_personal)——无需后验证
|       +-- 未找到? -> 进入阶段4(跳过阶段3)
|
+-- 阶段3: 是否为工作/教育邮箱且有公司名称和姓名部分?
|   +-- 是 -> crustdata_people_search_db(filters: name + current_employers.name)
|   |   +-- 找到且姓名验证通过? -> 阶段7评分校验 -> 完成(method=name+company)
|   |   +-- 未找到? -> 进入阶段4
|   +-- 否 -> 跳至阶段4
|
+-- 阶段4: 仍未匹配?(所有邮箱类型)
|   +-- crustdata_people_search_db(filters: column="emails", type="(.)", value=本地部分)
|   +-- 针对每个结果验证:
|   |   +-- 姓名部分匹配档案姓名?(必填)
|   |   +-- 工作/教育邮箱: 组织出现在档案经历中?(必填)
|   |   +-- 个人邮箱: 仅需姓名匹配(无组织可检查)
|   +-- 验证通过的匹配? -> 阶段7评分校验 -> 完成(method=email_contains)
|   +-- 无验证通过的匹配? -> 进入阶段5
|
+-- 阶段5: 是否为个人邮箱且有2个及以上姓名部分?
|   +-- 是 -> crustdata_people_search_db(filters: name="FirstName LastName")
|   |   +-- 返回1-3个结果且姓名验证通过? -> 阶段7评分校验 -> 完成(method=name_search)
|   |   +-- 返回0或4个及以上结果? -> 进入阶段6
|   +-- 否 -> 进入阶段6
|
+-- 阶段6: 仍未匹配?(所有邮箱类型)
|   +-- crustdata_web_search(query="EMAIL linkedin", sources=["web"])
|   |   +-- 找到linkedin.com/in/ URL? -> crustdata_people_enrich(linkedin_profile_url=URL) -> 阶段7评分校验 -> 完成
|   +-- 未找到URL? -> crustdata_web_search(query="who is EMAIL", sources=["ai"])
|   |   +-- 提取到个人姓名? -> crustdata_people_search_db(name) -> 获取档案URL -> 信息补全 -> 阶段7评分校验 -> 完成
|   +-- 未找到任何信息? -> 未匹配
|
+-- 阶段7: 评分校验(应用于阶段3-6的所有候选结果)
    +-- 必须同时满足: name_sim > 0.8 AND combined_score > 0.7
    +-- 通过? -> 接受匹配结果
    +-- 未通过? -> 拒绝,进入下一阶段或标记为未匹配

Key learnings

关键经验

  1. Verification is non-negotiable. In testing on 1,476 emails, strict verification removed 149 false positives that the unverified approach would have returned. Always verify.
  2. Phase 2 post-verification catches ~5% bad matches. The person enrich API sometimes returns the wrong person at the right company (e.g., a different employee). Employer domain + name-prefix checks catch these.
  3. AI web search correction works. When person enrich returns the right company but wrong person,
    crustdata_web_search
    with
    sources: ["ai"]
    and query "who is EMAIL" correctly identifies the real person. Recovered 7 matches in testing.
  4. Phase 4 has an extremely high false positive rate without verification. In testing: 1,540 rejections vs 37 accepts. The substring matching on the
    emails
    field produces many spurious matches. Strict name + company verification is essential.
  5. The
    emails
    field in PersonDB uses
    "column"
    not
    "filter_type"
    .
    Using the wrong key returns zero results silently.
  6. For work/edu emails, no company = no match. If Phase 1 didn't identify the company for a domain, do NOT accept Phase 4 results for emails at that domain. There's nothing to verify against.
  7. Phase 5 must verify names, not just count results. The old approach of "accept first result if <= 3 results" produces false positives like "Bert Zacharin" matching "Zacharie Bere". Both name parts from the email must appear in the profile name.
  8. Edu emails work with
    crustdata_people_enrich
    .
    Despite the parameter being called
    business_email
    , it matches faculty and staff at universities.
  9. crustdata_people_enrich
    params
    linkedin_profile_url
    and
    business_email
    are mutually exclusive.
    Cannot pass both in the same call.
  10. crustdata_people_search_db
    returns results in a
    profiles
    key
    , not
    data
    .
  11. The
    personal_email
    parameter (released April 2026) dramatically improves personal email coverage from ~5-10% to ~90%.
    Before this parameter, personal emails relied on Phase 4/5 substring searches with low precision. Now Phase 2 Branch B handles most personal emails directly.
  12. Phase 7 requires BOTH
    name_sim > 0.8
    AND
    combined_score > 0.7
    .
    This prevents a perfect company match from compensating for a bad name match. Without the
    name_sim
    hard gate, false positives like "Bert Zacharin" matching "Zacharie Bere" can slip through.


  1. 验证必不可少。在1476个邮箱的测试中,严格验证移除了149个未验证方法会返回的假阳性结果。务必进行验证。
  2. 阶段2后验证捕获约5%的错误匹配。个人信息补全API有时会返回同一公司的错误人员(如其他员工)。雇主域名+姓名前缀检查可捕获此类情况。
  3. AI网页搜索修正有效。当个人信息补全返回正确公司但错误人员时,使用
    sources: ["ai"]
    crustdata_web_search
    并查询“who is EMAIL”可正确识别真实人员。测试中找回了7个匹配结果。
  4. 阶段4无验证时假阳性率极高。测试中:1540个拒绝结果 vs 37个接受结果。
    emails
    字段的子串匹配会产生大量虚假匹配。严格的姓名+公司验证至关重要。
  5. PersonDB中的
    emails
    字段使用
    "column"
    而非
    "filter_type"
    。使用错误的键会静默返回零结果。
  6. 工作/教育邮箱若无公司信息则无匹配结果。若阶段1未识别到域名对应的公司,请勿接受该域名邮箱的阶段4结果,无验证依据。
  7. 阶段5必须验证姓名,而非仅统计结果数量。旧方法“若结果≤3则接受第一个结果”会产生假阳性,如“Bert Zacharin”匹配“Zacharie Bere”。邮箱中的两个姓名部分必须出现在档案姓名中。
  8. 教育邮箱可使用
    crustdata_people_enrich
    。尽管参数名为
    business_email
    ,但可匹配大学教职员工。
  9. crustdata_people_enrich
    linkedin_profile_url
    business_email
    参数互斥
    。同一调用中不能同时传递两者。
  10. crustdata_people_search_db
    的结果存储在
    profiles
    键中
    ,而非
    data
  11. personal_email
    参数(2026年4月发布)大幅提升个人邮箱覆盖率,从约5-10%提升至约90%
    。在此参数发布前,个人邮箱依赖阶段4/5的子串搜索,精度较低。现在阶段2分支B可直接处理大多数个人邮箱。
  12. 阶段7要求同时满足name_sim > 0.8和combined_score > 0.7。避免因公司匹配完美而忽略姓名匹配不佳的情况。若无
    name_sim
    硬性限制,“Bert Zacharin”匹配“Zacharie Bere”这类假阳性结果可能会被通过。


Person-to-Email Enrichment

个人转邮箱信息补全

When the input is a list of people (names, profile URLs, or both) and the goal is to find their email addresses, use this flow instead.

当输入为个人列表(姓名、档案URL或两者皆有),目标是查找其邮箱地址时,使用此流程。

Step 1: Resolve profile URLs

步骤1:解析档案URL

If the input already has profile URLs, skip this step.
If only names + companies are provided, resolve to profile URLs first:
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "Person Name"
      - filter_type: "current_employers.name"
        type: "(.)"
        value: "Company Name"
  page_size: 3
The result includes a
flagship_profile_url
field - that's the profile URL you need.
Fallback: If not found in PersonDB, try web search:
crustdata_web_search:
  query: "Person Name Company site:linkedin.com/in/"
Extract the profile URL from the top result.
若输入已包含档案URL,跳过此步骤。
若仅提供姓名+公司,先解析为档案URL:
crustdata_people_search_db:
  filters:
    op: "and"
    conditions:
      - filter_type: "name"
        type: "(.)"
        value: "Person Name"
      - filter_type: "current_employers.name"
        type: "(.)"
        value: "Company Name"
  page_size: 3
结果包含
flagship_profile_url
字段——这就是所需的档案URL。
备选方案:若在PersonDB中未找到,尝试网页搜索:
crustdata_web_search:
  query: "Person Name Company site:linkedin.com/in/"
从顶部结果中提取档案URL。

Common pitfalls

常见陷阱

  • Common names: always include company or title context. "Michael Ma Liquid 2 Ventures" not just "Michael Ma".
  • Name variants: try both formal and common names - "William Drevno" vs "Will Drevno", "Robert" vs "Bob".
  • Recently changed roles: search with both old and new company if you know them.

  • 常见姓名:务必包含公司或职位上下文。如“Michael Ma Liquid 2 Ventures”而非仅“Michael Ma”。
  • 姓名变体:尝试正式名和常用名——如“William Drevno” vs “Will Drevno”,“Robert” vs “Bob”。
  • 近期换岗:若知晓,同时搜索旧公司和新公司。

Step 2: Enrich business emails

步骤2:补全工作邮箱

Batch up to 25 profile URLs per call. You MUST include
business_email
in the fields parameter - it is not returned by default.
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1,https://linkedin.com/in/person2,..."
  fields: "name,business_email"
每次调用最多批量处理25个档案URL。必须在fields参数中包含
business_email
——默认不返回该字段。
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1,https://linkedin.com/in/person2,..."
  fields: "name,business_email"

Critical details

关键细节

  • Up to 25 comma-separated profile URLs per call
  • business_email
    must be explicitly requested in
    fields
  • linkedin_profile_url
    and
    business_email
    params are mutually exclusive - this call uses
    linkedin_profile_url
  • The response is an array. Map results back to input URLs using the
    linkedin_profile_url
    or
    linkedin_flagship_url
    field in each result.
  • 每次调用最多接受25个逗号分隔的档案URL
  • business_email
    必须在
    fields
    中明确请求
  • linkedin_profile_url
    business_email
    参数互斥——此调用使用
    linkedin_profile_url
  • 响应为数组,通过每个结果中的
    linkedin_profile_url
    linkedin_flagship_url
    字段将结果映射回输入URL。

Handling large lists

处理大型列表

For 25+ profiles, batch into groups of 25 and call sequentially. Parse responses with Python if they exceed token limits.

若超过25个档案,按25个一组分批处理。若响应超过令牌限制,使用Python解析。

Step 3: Enrich personal emails and phone numbers

步骤3:补全个人邮箱和电话号码

For profiles where you also need personal contact info, make a separate call with the personal contact fields:
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1,https://linkedin.com/in/person2,..."
  fields: "personal_contact_info.personal_emails,personal_contact_info.phone_numbers"
针对还需个人联系方式的档案,单独调用包含个人联系字段的接口:
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1,https://linkedin.com/in/person2,..."
  fields: "personal_contact_info.personal_emails,personal_contact_info.phone_numbers"

Credit usage

积分使用

  • 2 credits per profile for
    personal_contact_info.personal_emails
  • 2 credits per profile for
    personal_contact_info.phone_numbers
  • These are additive on top of the base enrichment cost
  • 每个档案2积分用于
    personal_contact_info.personal_emails
  • 每个档案2积分用于
    personal_contact_info.phone_numbers
  • 这些积分在基础信息补全成本之外累加

Access

权限

Personal contact info enrichment is access-controlled. Not all accounts have it enabled. If the fields come back empty, the account may need this feature turned on.
个人联系方式补全为权限控制功能。并非所有账户都已启用。若字段返回空,可能需要为账户开启此功能。

Combining with business email

与工作邮箱组合请求

You can request everything in one call:
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1"
  fields: "name,business_email,personal_contact_info.personal_emails,personal_contact_info.phone_numbers"

可在一次调用中请求所有信息:
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1"
  fields: "name,business_email,personal_contact_info.personal_emails,personal_contact_info.phone_numbers"

Step 4: GitHub fallback for personal emails (technical people)

步骤4:针对技术人员的GitHub个人邮箱备选方案

If personal contact info enrichment is not available or returns empty for technical people, fall back to GitHub commit history. This only works for engineers, developers, and technical founders.
若个人联系方式补不可用或针对技术人员返回空结果,可使用GitHub提交记录作为备选方案。仅适用于工程师、开发人员和技术创始人。

Find their GitHub username

查找GitHub用户名

crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1"
  fields: "github_profiles"
Or search the web:
crustdata_web_search:
  query: "Person Name Company site:github.com"
crustdata_people_enrich:
  linkedin_profile_url: "https://linkedin.com/in/person1"
  fields: "github_profiles"
或网页搜索:
crustdata_web_search:
  query: "Person Name Company site:github.com"

Verify the GitHub profile

验证GitHub档案

Confirm at least 2 of these match: GitHub bio mentions their company/role, profile name matches, repo topics align with their expertise.
确认至少以下两项匹配:GitHub简介提及公司/职位、档案姓名匹配、仓库主题与其专业领域一致。

Extract email from commits

从提交记录提取邮箱

Use
crustdata_web_fetch
to read the oldest non-fork repo's commits:
crustdata_web_fetch:
  url: "https://api.github.com/users/USERNAME/repos?sort=created&direction=asc&per_page=5"
Then fetch the commit email:
crustdata_web_fetch:
  url: "https://github.com/OWNER/REPO/commit/SHA.patch"
Extract the email from the
From:
header in the patch. Discard
noreply@github.com
and
*@users.noreply.github.com
addresses.

使用
crustdata_web_fetch
读取最早的非复刻仓库的提交记录:
crustdata_web_fetch:
  url: "https://api.github.com/users/USERNAME/repos?sort=created&direction=asc&per_page=5"
然后获取提交邮箱:
crustdata_web_fetch:
  url: "https://github.com/OWNER/REPO/commit/SHA.patch"
从补丁的
From:
头部提取邮箱。丢弃
noreply@github.com
*@users.noreply.github.com
地址。

Person-to-email expected results

个人转邮箱预期结果

  • Business emails: 95%+ of professionals at known companies
  • Personal emails: 95%+ via enrichment API with personal contact info enabled
  • Phone numbers: 95%+ via enrichment API with personal contact info enabled

  • 工作邮箱:已知公司的专业人员中95%+可匹配
  • 个人邮箱:启用个人联系方式补全API的账户中95%+可匹配
  • 电话号码:启用个人联系方式补全API的账户中95%+可匹配

MCP tool reference

MCP工具参考

ToolPurposeKey parameters
crustdata_company_identify
Domain to company (FREE)
company_website
(domain string)
crustdata_people_enrich
Email to person, or person to emails
business_email
OR
personal_email
OR
linkedin_profile_url
(mutually exclusive),
fields
,
enrich_realtime
crustdata_people_search_db
Search people by filters
filters
(object with
op
,
conditions
),
page_size
crustdata_web_search
Web search (find profiles, fallback)
query
(search string)
crustdata_web_fetch
Fetch page content (GitHub commits)
url
(page URL)
MCP server:
mcp.crustdata.com/mcp

工具用途关键参数
crustdata_company_identify
域名转公司(免费)
company_website
(域名字符串)
crustdata_people_enrich
邮箱转个人,或个人转邮箱
business_email
personal_email
linkedin_profile_url
(互斥)、
fields
enrich_realtime
crustdata_people_search_db
通过过滤条件搜索人员
filters
(包含
op
conditions
的对象)、
page_size
crustdata_web_search
网页搜索(查找档案、备选方案)
query
(搜索字符串)
crustdata_web_fetch
获取页面内容(GitHub提交记录)
url
(页面URL)
MCP服务器:
mcp.crustdata.com/mcp

Tool dependencies

工具依赖

This skill requires the Crustdata MCP server connected at mcp.crustdata.com/mcp. It provides:
  • crustdata_company_identify
  • crustdata_people_enrich
  • crustdata_people_search_db
  • crustdata_web_search
  • crustdata_web_fetch
此技能需要连接Crustdata MCP服务器mcp.crustdata.com/mcp。该服务器提供:
  • crustdata_company_identify
  • crustdata_people_enrich
  • crustdata_people_search_db
  • crustdata_web_search
  • crustdata_web_fetch