API reference
Full request and response specification for the Contact Enrichment API.
Base URL
https://api.goodfit.io/contactsAuthentication
Send your API key in the Authorization header on every request:
Authorization: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxThis is the same key used for the company enrichment API β your account manager will provide it. Keys are secrets: never expose one in client-side code.
401
Header missing, or not a valid key format.
403
Key not recognised, revoked, or lacking enrichment access.
Rate limits
20 requests per second, shared across all API keys belonging to your account. Exceeding it returns 429 with a Retry-After header giving the number of seconds to wait:
HTTP/1.1 429
Retry-After: 1
{ "error": "Rate limit exceeded. Retry after 1s" }Honour Retry-After and retry β a 429 is never a permanent failure.
POST /enrichment β submit a contact
At least one of identity.linkedInUrl or identity.workEmail is required. Everything else is optional. Matching on name alone is not supported.
identity.linkedInUrl
string
β
A LinkedIn profile URL. Any common form is accepted.
identity.workEmail
string
β
Must be a company address; personal email domains are rejected.
identity.companyName
string
β
Optional hint, used only to identify the right employment.
identity.companyDomain
string
β
Optional hint, reduced to a registrable domain. More reliable than companyName.
identity.selectedCompanyId
string
β
The employment you picked from a MULTIPLE_EMPLOYMENTS_FOUND response.
externalIds.salesforce
string
β
15 or 18 character ID. 15 character IDs are expanded to 18.
externalIds.hubspot
string | number
β
Numeric ID.
enrichPhoneNumber
boolean
false
Request a mobile phone number as well as an email.
syncToCRM
boolean
false
Write the enriched contact back to your connected CRM.
Both flags default to false, so a request that omits them enriches the email only and leaves your CRM untouched. You need a connected integration for each externalIds provider you supply.
A successful submission returns 201:
Submitting the same contact twice is safe. If an identical request is still in progress you get
200with the existingrequestIdrather than a second request, so a retrying integration never duplicates work or spends credits twice. Poll the returnedrequestIdeither way.
Changing enrichPhoneNumber or syncToCRM makes it a different request, so asking for a phone number after an email-only request is always honoured.
400
Body is not valid JSON, neither identifier was supplied, or a field is invalid.
402
Out of credits β nothing was queued or charged.
429
Rate limit exceeded.
A 402 is returned before any work starts, so retrying after a top-up is safe. Asking for a phone number requires both contact and phone credits. A 400 names the problem:
GET /enrichment/{requestId} β read a request
QUEUED
Accepted, not started yet.
IN_PROGRESS
Being enriched.
COMPLETE
Finished β result is present.
ERROR
Failed β error explains why. Safe to resubmit.
result appears only when COMPLETE, and error only when ERROR.
A few fields are worth calling out:
inputsechoes back your identity in the canonical form used for matching, so you can confirm what was actually looked up.startDateis the start of the employment, asYYYY-MM-DD.isFormerEmploymentistruewhen the person has left the company you asked about. We still resolve them, so you can see who they were and act on the change.addedToDatasetistruewhen this request added the record to your dataset, andfalsewhen it was already there.company.addedToDatasetbeingtruemeans the company was new to you, so we added it and charged one enrichment credit. A newly added company appears immediately with core firmographics only; scores, segments and the rest of its data arrive with your next daily sync.
Note β
personasandmarketsare part of the contract but not yet populated β persona and market matching for this API are still being built. They are empty rather thanNOT_FOUND, so you can tell "not built yet" from "we looked and found nothing".
400
requestId is missing or not a UUID.
404
No such request under your account.
429
Rate limit exceeded.
Email and phone values
Every resolved contact is enriched for a work email. A mobile number is looked up only when you set enrichPhoneNumber. Three sentinel values tell the cases apart:
NOT_FOUND
Searched, nothing available.
NOT_REQUESTED
Not asked for (mobilePhone only).
SKIPPED_EX_EMPLOYEE
Skipped, the resolved employment is one they have left (workEmail only).
Enrichment is best effort. If a lookup fails we still return the resolved contact, with NOT_FOUND in place of the value β the request doesn't fail.
Returned emails carry the same verification statuses as every other email GoodFit supplies β see Email and Phone enrichment.
When we couldn't resolve someone
found is false and reason says why. COMPLETE still means we finished β only ERROR means something went wrong on our side.
reason
Meaning
PERSON_NOT_FOUND
No person matches the identity you gave.
MULTIPLE_EMPLOYMENTS_FOUND
We found the person but they hold several roles.
NO_COMPANY_MATCH
We found the person but couldn't tie them to a company we can serve.
NOT_ENOUGH_INFORMATION
The identity was too thin to search on.
COMPANY_HAS_NO_DOMAIN
Their employer has no website domain β see below.
COMPANY_RESOLUTION_CONFLICT
The employer we found conflicts with the CRM record you supplied.
CONTACT_RESOLUTION_CONFLICT
The CRM record you supplied belongs to a different person.
You aren't charged when we can't deliver a contact.
MULTIPLE_EMPLOYMENTS_FOUND is the one reason you can act on directly β see Handling multiple employments.
The two conflict reasons mean your request and your CRM disagree about who or where this person is. Rather than guess, we refuse β it's worth resolving in the CRM before retrying.
COMPANY_HAS_NO_DOMAIN
We identified the person, but their employer has no website domain β commonly a LinkedIn placeholder page (for example "Confidential Government") or a subsidiary with no site of its own. Companies are keyed on domain in your dataset, so this one can't be added and the contact couldn't be attached to it.
Retrying won't help. If the person in fact works at a company you can name, supply that companyDomain.
Syncing to your CRM
Set syncToCRM: true and we write the enriched contact to every connected CRM that has contact sync enabled, normally within five minutes of submission. The result carries syncedToCRM and, under externalIds, the record ID in each CRM.
How we choose the record to write to:
Send an
externalIdsvalue we recognise and we update that exact record.Send one we've never seen and we adopt it and update it, rather than creating a second contact.
Send none and we look for an existing contact on the work email β using whichever CRM field you have mapped to email in Data Points β and only create a new record when there's no match.
Two cases are deliberately refused rather than guessed at, and appear on your Connector Logs page:
the ID you sent points at a different record from the one we already have this person linked to;
several contacts in your CRM share the email, so there's no single right record to update.
Note β We search your CRM by mapped email field only
Last updated