Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.crustdata.com/llms.txt

Use this file to discover all available pages before exploring further.

GET /screener/person/enrich
POST /person/enrich
This page lists every behavioral and contract change between the legacy person-enrich endpoint (called without enrich_realtime=true, i.e. the cached/indexed path) and the current one. Use it as a side-by-side reference when porting an integration; each section gives the old shape, the new shape, and the smallest edit that closes the gap.
TopicLegacyCurrent
PathGET /screener/person/enrichPOST /person/enrich
MethodGET with query parametersPOST with a JSON body
AuthAuthorization: Token <key>Authorization: Bearer <key>
Version(not required)x-api-version: 2025-11-01 (required)
Base URLhttps://api.crustdata.comhttps://api.crustdata.com
Three header / shape changes are required. The HTTP method moved from GET to POST, the authorization scheme moved from Token <key> to Bearer <key>, and the x-api-version: 2025-11-01 header is now required on every call. Calls missing any of these are rejected.
This guide covers the indexed (cached) path of the legacy endpoint — requests sent without enrich_realtime=true. The current /person/enrich endpoint serves the indexed dataset only; on-demand live retrieval is not part of this endpoint. See Removed features for what happened to enrich_realtime.

1. Request — top-level keys

Identifiers moved from comma-separated query parameters to JSON arrays in the request body. Submit exactly one identifier type per request (up to 25 values).
Legacy query parameterCurrent body keyNotes
?linkedin_profile_url=https://...,https://...{ "professional_network_profile_urls": ["https://...", "..."] }Key renamed. Comma-separated string → JSON array.
?business_email=jane@example.com{ "business_emails": ["jane@example.com"] }Single value → JSON array. Now supports batch lookups (up to 25 values).
?github_profile_url=https://github.com/...(removed from this endpoint)Dev-platform identifiers moved to a dedicated endpoint — see Removed features.
?fields=name,headline,summary{ "fields": ["basic_profile.name", "basic_profile.headline"] }Comma-separated string → JSON array. Section names changed — see Field-name mapping.
?min_similarity_score=0.8{ "min_similarity_score": 0.8 }Moved into the body. Same 0..1 range.
?preview=true{ "preview": true }Moved into the body. Same semantics — returns basic-profile fields only and charges 0 credits.
?enrich_realtime=true(removed)No direct replacement on this endpoint — see Removed features.
?force_fetch=true(removed)Tied to enrich_realtime; removed alongside it.
?open_to_cards=true(removed)Tied to enrich_realtime; removed alongside it.
?colleagues=true(removed)Colleague expansion is no longer part of person enrich.
?enrichment_method=salesnavigator|voyager(removed)Internal routing flag — no replacement.
Submit exactly one identifier type per request. Mixing professional_network_profile_urls and business_emails in the same call returns 400. Maximum 25 values per identifier array.

2. Identifier behavior

TopicLegacyCurrent
Identifier shapeComma-separated string in a query parameterJSON array in the body
Identifier typesProfessional-network URL, business email, dev-platform URLProfessional-network URL, business email
Max values per request2525
Mutual exclusionPass one identifier parameterPass one identifier array
Business emailSingle value (business_email=jane@…)Array (business_emails: ["jane@…"]) — supports batch
Mixed-identifier error400 with a free-form error string400 with { error: { type: "invalid_request", message: "..." } }
Over-limit error400 with a free-form error string400 with the new error envelope

3. Field-name mapping

The legacy response was a flat object with top-level fields like linkedin_profile_url, name, headline, current_employers, and github_profiles. The current response groups fields under basic_profile, professional_network, social_handles, contact, experience, education, skills, certifications, honors, and dev_platform_profiles.

Identity and profile

Legacy fieldCurrent field
person_idcrustdata_person_id (top-level)
namebasic_profile.name
titlebasic_profile.current_title
headlinebasic_profile.headline
summarybasic_profile.summary
location (string)basic_profile.location.raw (plus parsed city, state, country, continent)
languages[]basic_profile.languages[]
profile_picture_urlprofessional_network.profile_picture_url
profile_picture_permalinkbasic_profile.profile_picture_permalink / professional_network.profile_picture_permalink
last_updatedbasic_profile.last_updated (plus top-level updated_at)
enriched_realtime(removed — this endpoint serves indexed data only)
query_linkedin_profile_urn_or_slug[](removed; the input URL is echoed back as matched_on — see Response shape)

Professional-network attributes

Legacy fieldCurrent field
linkedin_profile_urlsocial_handles.professional_network_identifier.profile_url
linkedin_flagship_urlsocial_handles.professional_network_identifier.profile_url (canonical URL only — flagship variant is no longer surfaced separately)
num_of_connectionsprofessional_network.connections
num_of_followersprofessional_network.followers
linkedin_joined_dateprofessional_network.joined_date
linkedin_verifications[]professional_network.verifications[]
linkedin_open_to_cards[]professional_network.open_to_cards[] (only populated for opted-in profiles)

Social handles

Legacy fieldCurrent field
twitter_handlesocial_handles.twitter_identifier.slug
github_profiles[].urlsocial_handles.dev_platform_identifier.profile_url (primary handle); full list in dev_platform_profiles[]

Skills

Legacy fieldCurrent field
skills[]skills.professional_network_skills[] (plus skills.endorsed_skills[] when available)

Experience and employers

The legacy response carried four parallel arrays (current_employers, past_employers, all_employers, all_employers_company_id, all_titles). The current response consolidates these under experience.employment_details, with one entry per employment record.
Legacy fieldCurrent field
current_employers[]experience.employment_details.current[]
past_employers[]experience.employment_details.past[]
all_employers[] (company names)(derive from experience.employment_details.{current,past}[].name)
all_employers_company_id[](derive from experience.employment_details.{current,past}[].crustdata_company_id)
all_titles[](derive from experience.employment_details.{current,past}[].title)

Education

Legacy fieldCurrent field
education_background[]education.schools[]
all_schools[](derive from education.schools[].school_name)
all_degrees[](derive from education.schools[].degree)

Contact

Legacy fieldCurrent field
email[]contact.business_emails[] (verified business addresses) and contact.personal_emails[] (personal addresses) — each entry now carries email, status (verified / unverified), and last_updated.
(new)contact.phone_numbers[]
(new)contact.websites[]

Dev-platform profiles

Legacy fieldCurrent field
github_profiles[]dev_platform_profiles[] — neutral name; same one-entry-per-profile shape. To enrich a person by a dev-platform URL only, use the dedicated dev-platform endpoint.
For the full enrich-response field catalog, see Person Enrich reference.

4. Type changes

FieldLegacy typeCurrent typeNotes
email[]array of stringsarray of objectsEach contact email is now { email, status, last_updated } rather than a bare string. Split between contact.business_emails[] and contact.personal_emails[].
locationstringobjectThe raw string is preserved at basic_profile.location.raw; parsed city, state, country, continent are also returned.
business_email requestsingle stringarray of stringsThe request key is now plural (business_emails) and accepts up to 25 values.
linkedin_profile_url requestcomma-separated stringarray of stringsRenamed to professional_network_profile_urls and now a JSON array.

5. Removed features

enrich_realtime, force_fetch, open_to_cards

The legacy endpoint used enrich_realtime=true to fall back to real-time retrieval from the web when a person was not in the indexed dataset, with force_fetch=true bypassing the cache and open_to_cards=true pulling additional opt-in fields. These flags have been removed from /person/enrich — the current endpoint serves the indexed dataset only. If a person is not present, the request returns an entry with empty matches: [] rather than attempting a real-time fetch. If you previously called the legacy endpoint with enrich_realtime=true, use the dedicated live-enrich endpoint instead — see Person Live Enrich. It performs real-time retrieval and supports the same identifier shape as /person/enrich.

github_profile_url identifier

The legacy endpoint accepted a comma-separated list of dev-platform profile URLs as an identifier. This input mode has been removed from /person/enrich. Dev-platform profiles are still returned in the response under dev_platform_profiles[] when present, but a separate endpoint is used to enrich a person starting from a dev-platform URL — see Person Dev-Platform reference.

colleagues

The legacy colleagues=true flag expanded the response with a list of the target person’s colleagues. This feature has been removed; build a colleague list explicitly by calling Person Search with the appropriate company filter.

enrichment_method

The legacy enrichment_method parameter (salesnavigator / voyager) routed requests to specific upstream collection paths. This parameter has been removed — routing is handled internally.

enriched_realtime response flag

The legacy enriched_realtime boolean indicated whether a record was served from cache or fetched live. Because /person/enrich serves the indexed dataset only, this flag has been removed from responses.

Comma-separated query parameter style

The endpoint moved from GET with query parameters to POST with a JSON body. All identifiers must be sent as JSON arrays.

6. Added features

FeatureDescription
Match-result envelopeThe response wraps each result in { matched_on, match_type, matches: [{ confidence_score, person_data }] }, making batch enrichment unambiguous.
confidence_scoreNumeric score on each match candidate. Higher is better; 1.0 is common for direct identifier lookups.
Batch business-email lookupsbusiness_emails is now an array (up to 25). The legacy endpoint accepted a single email per call.
Parsed location objectReturns city, state, country, continent, and raw rather than just the unparsed string.
Structured email entriescontact.business_emails[] and contact.personal_emails[] carry { email, status, last_updated } instead of bare strings.
contact.phone_numbers[], contact.websites[]New contact channels surfaced on enrich.
Stable error envelopeAll 4xx / 5xx errors return { "error": { "type", "message", "metadata" } }.

7. Response shape

The envelope shape changed. The legacy endpoint returned a top-level array of person records. The current endpoint returns a top-level array of match-result envelopes, each containing one or more person_data candidates.

Envelope

[
    {
        "person_id": 14540,
        "name": "David Hsu",
        "title": "Founder, CEO",
        "linkedin_profile_url": "https://www.linkedin.com/in/dvdhsu/",
        "location": "San Francisco, California, United States",
        "num_of_connections": 4321,
        "skills": ["Python", "React"],
        "email": ["david@example.com"],
        "current_employers": [
            { "employer_name": "Retool", "title": "Founder, CEO" }
        ],
        "enriched_realtime": false
    }
]

Match-result fields

FieldDescription
matched_onThe input value you submitted (a profile URL or business email).
match_typeOne of professional_network_profile_url, business_email.
matchesArray of candidate matches. Empty when nothing matched.
matches[].confidence_scoreNumber. Higher is better. 1.0 is common for direct identifier lookups.
matches[].person_dataFull enriched person profile — see Field-name mapping for section paths.

No-match behavior

Each entry corresponds to one input identifier. When nothing matches, the entry’s matches array is empty:
[
    {
        "matched_on": "no-such-person@example.invalid",
        "match_type": "business_email",
        "matches": []
    }
]
The legacy endpoint returned 404 when no profiles matched. The current endpoint returns 200 with empty matches: [] for unmatched identifiers. The OpenAPI contract still defines 404 for completeness — handle both.

8. Error responses

The error envelope changed shape. Status codes are unchanged (400, 401, 403, 500).
{
    "error": "Please provide exactly one of linkedin_profile_url, business_email, or github_profile_url",
    "error_code": "INVALID_INPUT"
}
401 continues to use a flat { "message": "Invalid API key in request" } shape — parse based on HTTP status.

9. End-to-end example

Enriching one person by professional-network URL — written against both endpoints.
curl --request GET \
  --url 'https://api.crustdata.com/screener/person/enrich?linkedin_profile_url=https://www.linkedin.com/in/dvdhsu/&fields=name,headline,current_employers' \
  --header 'authorization: Token YOUR_API_KEY'
Enriching by business email — note that the request key is now plural and accepts an array:
curl --request GET \
  --url 'https://api.crustdata.com/screener/person/enrich?business_email=jane@example.com' \
  --header 'authorization: Token YOUR_API_KEY'

Migration checklist

  • Switch from GET /screener/person/enrich (query params) to POST /person/enrich (JSON body).
  • Change the Authorization scheme from Token to Bearer.
  • Add x-api-version: 2025-11-01 header to every request.
  • Rename identifier keys: linkedin_profile_urlprofessional_network_profile_urls, business_emailbusiness_emails.
  • Convert comma-separated identifier values to JSON arrays. Wrap single business emails in an array.
  • Drop enrich_realtime=true, force_fetch=true, and open_to_cards=true — the new endpoint serves indexed data only. For real-time retrieval, use Person Live Enrich instead.
  • Drop colleagues=true — build colleague lists with Person Search instead.
  • Drop enrichment_method — routing is handled internally.
  • Drop github_profile_url as an identifier — see Person Dev-Platform reference for dev-platform-rooted enrichment.
  • Convert fields from a comma-separated string to a JSON array, and migrate field names to the new section-prefixed paths (see Field-name mapping).
  • Unwrap each result from the new { matched_on, match_type, matches: [{ confidence_score, person_data }] } envelope.
  • Update parsers for the nested person_data shape: namebasic_profile.name, titlebasic_profile.current_title, headlinebasic_profile.headline, summarybasic_profile.summary.
  • Update location parsing — it is now an object with raw, city, state, country, continent rather than a single string.
  • Update email[] parsing — emails are now objects ({ email, status, last_updated }) and split between contact.business_emails[] and contact.personal_emails[].
  • Map current_employers[] / past_employers[] to experience.employment_details.current[] / experience.employment_details.past[]. Derive all_employers, all_titles, all_employers_company_id from these arrays if you still need them.
  • Map education_background[] to education.schools[].
  • Map linkedin_profile_url (response) to social_handles.professional_network_identifier.profile_url, twitter_handle to social_handles.twitter_identifier.slug.
  • Map num_of_connections to professional_network.connections, skills[] to skills.professional_network_skills[].
  • Map github_profiles[] to dev_platform_profiles[].
  • Map person_id to crustdata_person_id.
  • Handle no-match as matches: [] (legacy returned 404).
  • Update error handlers for the new error.type / error.message envelope.
  • Drop any parsing of enriched_realtime from the response — the field no longer exists.

See also