Find contacts using filters
ZoomInfo's GTM Data API helps you identify the right prospects through targeted search and enrichment. In this tutorial, you'll learn how to "find Marketing Directors at Software companies headquartered in California".
Credits Usage: Lookup and Search requests do not consume credits. Credits are only charged when you enrich a contact, and only the first time that contact is enriched.
This workflow uses four Data API v1 endpoints in the recommended sequence to build a targeted contact list.
- Lookup: Resolves valid values for
industries,states,management-levels, andjob-functions. - Search Contacts: Finds Marketing Directors at Software companies in California and returns matching
personIds with a match score. - Enrich Contacts: Retrieves verified contact details, including business email and phone, for the selected
contactId.
flowchart LR
A["Lookup
Resolve valid filter values"]
-->
B["Search Contacts
Find matching contacts"]
-->
C["Enrich Contacts
Retrieve verified details"]
cURL Request
Step 1: Resolve filter with Lookup
Endpoint: GET /data/v1/lookup/{fieldName}
fieldName is a path parameter. For this scenario you need four fields: industries, states, management-levels, and job-functions.
Example: Resolve the management level
curl --request GET \
--url 'https://api.zoominfo.com/gtm/data/v1/lookup/management-levels' \
--header 'accept: application/vnd.api+json' \
--header 'authorization: Bearer <access_token>'{
"data": [
{ "type": "ManagementLevel", "id": "Director", "attributes": { "name": "Director" } },
{ "type": "ManagementLevel", "id": "VP Level Exec", "attributes": { "name": "VP Level Exec" } },
{ "type": "ManagementLevel", "id": "C Level Exec", "attributes": { "name": "C Level Exec" } }
]
}Step 2: Search Contacts Using Lookup Results
Endpoint: POST /data/v1/contacts/search
This is the key thing to know about this endpoint: you don't need to search companies first. Search Contacts accepts company-level filters (industryCodes, state, metroRegion, employeeCount, revenue, companyName, etc.) in the exact same request as person-level filters (managementLevel, jobFunction, jobTitle). ZoomInfo matches both sides internally in one call — you just pass every filter you have.
This call is free and returns only match hints (hasEmail, hasDirectPhone, etc.) — no actual email addresses or phone numbers yet.
curl --request POST \
--url 'https://api.zoominfo.com/gtm/data/v1/contacts/search?page%5Bnumber%5D=1&page%5Bsize%5D=25&sort=-contactAccuracyScore' \
--header 'accept: application/vnd.api+json' \
--header 'content-type: application/vnd.api+json' \
--header 'authorization: Bearer <access_token>' \
--data '
{
"data": {
"type": "ContactSearch",
"attributes": {
"industryCodes": "Computer Software",
"state": "California",
"locationSearchType": "HQ",
"managementLevel": "Director",
"jobFunction": "Marketing",
"primaryIndustriesOnly": true,
"contactAccuracyScoreMin": "80",
"requiredFields": "email,phone"
}
}
}'{
"data": [
{
"type": "Contact",
"id": "4191419698",
"attributes": {
"firstName": "Jordan",
"lastName": "Reyes",
"jobTitle": "Director of Marketing",
"contactAccuracyScore": 95,
"hasEmail": true,
"hasDirectPhone": false,
"hasMobilePhone": true,
"company": {
"id": 346572700,
"name": "Acme Software Inc"
}
}
}
],
"meta": {
"page": { "number": 1, "total": 1 },
"totalResults": 14
}
}Collect id (contactId) for every contact you want full detail on, that's the input to Step 3.
Step 3: Enrich the contacts returned by the search
Endpoint: POST /data/v1/contacts/enrich
This is the only step in the workflow that charges credits — one per record, unless the record is already under management in your org's 12-month window. Enrich up to 25 records per call.
curl --request POST \
--url 'https://api.zoominfo.com/gtm/data/v1/contacts/enrich' \
--header 'accept: application/vnd.api+json' \
--header 'content-type: application/vnd.api+json' \
--header 'authorization: Bearer <access_token>' \
--data '
{
"data": [
{ "type": "ContactEnrich", "attributes": { "personId": "4191419698" } },
{ "type": "ContactEnrich", "attributes": { "personId": "4191419712" } }
],
"outputFields": [
"firstName", "lastName", "jobTitle", "managementLevel",
"email", "phone", "mobilePhone", "companyName", "contactAccuracyScore"
],
"requiredFields": ["email"]
}'{
"data": [
{
"type": "Contact",
"id": "4191419698",
"attributes": {
"firstName": "Jordan",
"lastName": "Reyes",
"jobTitle": "Director of Marketing",
"managementLevel": ["Director"],
"email": "[email protected]",
"phone": "+1-650-555-0142",
"mobilePhone": "+1-650-555-0198",
"company": { "id": 346572700, "name": "Acme Software Inc" },
"contactAccuracyScore": 95
},
"meta": {
"input": { "personId": 4191419698 },
"matchStatus": "FULL_MATCH"
}
},
{
"id": "4191419712",
"type": "Contact",
"attributes": {},
"meta": {
"input": { "personId": 4191419712 },
"matchStatus": "NO_MATCH"
}
}
]
}Error Handling
| Status | Code | Message | What it means here |
|---|---|---|---|
| 400 | PFAPI0006 | Input parameter '[parameter]' is invalid | Usually means a value like industryCodes or managementLevel didn't come from Lookup. Re-run Step 1 and use the exact returned value. |
| 400 | PFAPI0004 | Missing required inputs... | Search or Enrich body didn't include enough identifying fields. |
| 401 | ZI0001 | Token invalid / expired | Refresh your OAuth token and retry. |
| 403 | ZI0003 | You do not have access to this endpoint | Your package doesn't include this field/endpoint — contact your ZoomInfo Account Manager. |
| 429 | ZI0004 | Rate limit exceeded | Back off exponentially and retry. |
Full reference: error handling.
Rate Limits
Default: 25 requests/second. Premium add-on: 30/sec. Premium+: 35/sec. All 429/5xx responses should be retried with exponential backoff. See rate limits.
Updated 2 days ago