Personnel
Find decision-maker contacts with email, LinkedIn, title, and seniority data.
(1) lead_type is required and changes what every field means. With lead_type=company, company_* fields describe the contact's employer. With lead_type=broker, broker_office_* fields describe where they work and company_* fields echo the broker's own corporate entity (not a client). Read field names as "the entity context for this contact."
(2) seniority and job_function are loose buckets. seniority=C-suite includes EAs and Senior Specialists adjacent to C-suite because matching fires on title substrings. job_function only filters on an allowlist — shorthands like HR or Benefits return 400 VALIDATION_ERROR. Confirm valid labels with GET /v1/filter-options/job_function.
(3) /autocomplete returns lightweight personnel suggestions, but its id field is currently empty. For name-to-ID resolution or a full record, use GET /v1/personnel?search=<name>&lead_type=…&page_size=1.
List Personnel
/v1/personnel0.50 / 2.00 per rowReturns a paginated list of personnel contacts matching your filters.
Pagination & format
Common to all endpoints — see Pagination and Response Modes.
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (0-indexed) |
page_size | integer | Results per page (max 500). Over-max → 400. |
sort_by | string | Column to sort by — allowlist: last_name (default), company_name, total_number_of_employees |
sort_dir | asc | desc | Sort direction |
mode | basic | full | Response verbosity. Advanced-only fields require advanced access. |
fields | string | Comma-separated field list (id always included; advanced field with basic key → 403) |
format | json | flat | Response format (flat for Clay/spreadsheets) |
count_only | boolean | Return only total count, no data rows (0 credits) |
include_count | boolean | Include total_count in meta (default true; false skips per-page COUNT) |
dry_run | boolean | Validate query and return estimated rows + credits (0 credits) |
Basic response fields
| Field | Type | Description |
|---|---|---|
lead_source | object | Attribution stamp: { source: "hillwinds", retrieved_at: ISO-8601 UTC }. retrieved_at is when this row was assembled for the response, not the same as last_updated_at, which is when the row was last refreshed upstream. |
tags | string[] | Your tag names for this contact (returned by GET /v1/personnel/:id with mode=full). Write with POST /v1/tags (entity_type=personnel); filter with tags=. |
Filters
| Parameter | Type | Description |
|---|---|---|
search | string | Fuzzy name search across full_name and title |
lead_type | company | broker | Required. Determines whether the contact sits at a company or a broker office |
seniority | string | Comma-separated: C-suite, VP-Director, Mid-Level, Entry-Level |
job_function | string | Bucketed job function category (e.g. HR, Finance, Operations) |
job_title | string | Free-text title search |
has_email | boolean | Contacts with verified email |
has_phone | boolean | Contacts with phone (advanced tier) |
has_linkedin | boolean | Contacts with LinkedIn URL |
form5500_role | string | Comma-separated Form 5500 roles (Plan Administrator, Sponsor, etc.) |
states | string | Company-side state codes |
dmas | string | Company-side DMA names |
geo_mode | state | dma | Company-side geographic mode (default state) |
person_states | string | State codes for the person's own location. Applies on its own — no person_geo_mode required (defaults to state). |
person_dmas | string | DMA names for the person's own location. Set person_geo_mode=dma to use these instead of person_states. |
person_geo_mode | state | dma | Optional. Only needed to pick dma when both person_states and person_dmas are supplied. |
company_eins | string | Comma-separated company EINs (hierarchical drill-down for company personnel) |
broker_office_ids | string | Comma-separated broker office IDs (hierarchical drill-down for broker personnel) |
broker_ids | string | Comma-separated broker IDs. Resolves to all of the broker's offices, returning everyone at that broker in one call (no broker → offices → personnel two-step). |
industries | string | Industry names (company personnel only) |
employee_bands | string | Company employee bands (company personnel only) |
funding_status | Self-Funded | Fully Insured | Company funding status (company personnel only) |
renewal_month | string | Company renewal month (company personnel only) |
filing_year | string | Filing year or 'latest' (default 'latest') |
short_form | boolean | Short-form filing flag (company personnel only) |
long_tail | boolean | Long-tail company flag (company personnel only) |
long_tail_brokers | boolean | Long-tail broker flag (broker personnel only) |
signals | string | Active signals on the parent entity (advanced tier only) |
signal_match | any | all | Match semantics when multiple signals= are passed (default any) |
broker | string | Broker name on the parent entity (substring match, company personnel only) |
broker_office | string | Broker office name on the parent entity (company personnel only) |
carrier | string | Carrier on the parent entity (company personnel only) |
benefit_type | string | Insurance line on the parent entity (company personnel only) |
tags | string | Tag names (AND logic) |
crm_status | string | in_hubspot | in_salesforce | in_both | not_in_crm |
cf[column] | string | Column filter — supports gte:/lte: for numeric columns |
eb[column] | boolean | Exclude blanks for column; only true is supported (false returns 400) |
lead_type=company or lead_type=broker. Missing returns 400 VALIDATION_ERROR with issue: missing_required. The parameter changes which fields are populated and which hierarchy applies (company vs broker office), so a default would silently bias results.Example
curl -H "Authorization: Bearer ss_live_..." \
"https://api.hillwinds.ai/v1/personnel?lead_type=company&states=CA&seniority=C-suite,VP-Director&has_email=true&page_size=5"{
"ok": true,
"data": [
{
"id": "per_8a3f21c",
"first_name": "Jennifer",
"last_name": "Smith",
"full_name": "Jennifer Smith",
"title": "VP of Human Resources",
"job_description": "Oversees HR and benefits administration",
"seniority": "VP-Director",
"email_address": "jennifer.smith@pacifichealthsystems.com",
"email_status": "verified",
"person_linkedin_url": "https://linkedin.com/in/jsmith",
"lead_type": "company",
"company_name": "PACIFIC HEALTH SYSTEMS INC",
"company_ein": "84629153",
"company_city": "SAN FRANCISCO",
"company_state": "CA",
"company_industry": "Hospitals",
"company_website": "pacifichealthsystems.com",
"company_linkedin": "https://linkedin.com/company/pacific-health-systems",
"broker_name": null,
"broker_office_id": null,
"broker_office_city": null,
"broker_office_state": null,
"form5500_role": "Plan Administrator",
"person_city": "San Francisco",
"person_state": "CA",
"person_dma": "SAN FRANCISCO",
"crm_status": [],
"lead_source": {
"source": "hillwinds",
"retrieved_at": "2026-06-24T14:16:00Z"
}
}
],
"meta": {
"total_count": 2891,
"page": 0,
"page_size": 1,
"returned": 1,
"has_more": true,
"response_tier": "basic",
"key_tier": "advanced",
"credits_charged": 0.50
}
}Batch Lookup
/v1/personnel/batch0.50 / 2.00 per matched rowFind contacts for multiple companies at once. Provide up to 50 company EINs and standard filters (lead_type, seniority, has_email, etc.); the API returns up to page_size contacts per company.
| Parameter | Type | Description |
|---|---|---|
company_eins | string[] | EINs of companies to enrich. Provide this or ids. |
ids | string[] | Personnel IDs to fetch directly. Provide this or company_eins. |
lead_typerequired | company | broker | Required. Accepted in the body OR as a query string — same rule as the list endpoint. |
page_size | integer | Max contacts to return per input. Default 5, capped by the list endpoint's page_size limit. |
mode, fields, and format are accepted as query string parameters (not body fields) — same semantics as on the list endpoint.
curl -X POST -H "Authorization: Bearer ss_live_..." \
-H "Content-Type: application/json" \
-d '{
"company_eins": ["123456789", "987654321"],
"lead_type": "company",
"page_size": 5
}' \
"https://api.hillwinds.ai/v1/personnel/batch?mode=full"Get Contact
/v1/personnel/:id0.50 / 2.00 per rowReturns a single contact by ID. Unlike the other detail endpoints, the spec lists mode, fields, format, and (required) lead_type here — pass lead_type=company|broker matching the contact's record, or the call returns 400.
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Personnel ID (path). |
lead_typerequired | company | broker | Must match the side the contact sits on. |
mode | basic | full | Field verbosity (same semantics as the list endpoint). |
fields | string | Comma-separated field allowlist. |
format | json | flat | Response format. |
curl -H "Authorization: Bearer ss_live_..." \
"https://api.hillwinds.ai/v1/personnel/per_a1b2c3d4?lead_type=company&mode=full"List Tags for a Contact
/v1/personnel/:id/tags0 creditsReturns the tags you've assigned to a single contact — the per-entity read of the tags you write with POST /v1/tags (entity_type=personnel). The same tags also appear inline as the tags field on GET /v1/personnel/:id?mode=full, and drive the tags= list filter. User-scoped: only your (or your API key's) tags are returned.
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Personnel ID (path). |
curl -H "Authorization: Bearer ss_live_..." \
"https://api.hillwinds.ai/v1/personnel/per_a1b2c3d4/tags"{
"ok": true,
"data": [
{
"id": "tag_9f2b",
"entity_id": "per_a1b2c3d4",
"entity_type": "personnel",
"tag": "q1-outreach",
"created_at": "2026-07-01T18:22:00Z"
}
],
"meta": { "returned": 1, "total_count": 1, "has_more": false }
}Reverse lookup by email or LinkedIn URL
Personnel has no dedicated /lookup endpoint. To find a single contact by a unique identifier you already have — a verified email or a person's LinkedIn URL — use the list endpoint with a cf[column]= exact-match filter on the response field of the same name. Useful for CRM enrichment when the inbound row has an email or LinkedIn URL but no Hillwinds ID.
# By email
curl -H "Authorization: Bearer ss_live_..." \
"https://api.hillwinds.ai/v1/personnel?lead_type=company&cf%5Bemail_address%5D=jennifer.smith%40pacifichealthsystems.com&page_size=1"
# By LinkedIn URL (URL-encode the value — / and : must become %2F and %3A)
curl -H "Authorization: Bearer ss_live_..." \
"https://api.hillwinds.ai/v1/personnel?lead_type=company&cf%5Bperson_linkedin_url%5D=https%3A%2F%2Flinkedin.com%2Fin%2Fjsmith&page_size=1"Match semantics. cf[email_address]= and cf[person_linkedin_url]= are exact-equality filters — case-sensitive, full string. Mistype a character and you get an empty list (not a 404). For a fuzzy name match, use ?search= on full_name instead.
lead_type=company or lead_type=broker must be set. If you don't know which side the contact sits on, query both — a contact at a company won't appear under lead_type=broker and vice versa.cf[email_address]= and cf[person_linkedin_url]= use the response field names verbatim. Don't use cf[email]= or cf[linkedin]= — those columns don't exist and return 400 VALIDATION_ERROR.For bulk CRM enrichment
If you have a list of emails or LinkedIn URLs to reverse-lookup, loop the request per-row — there's no batched email/LinkedIn lookup. For employer-side bulk enrichment (multiple EINs), use POST /v1/personnel/batch instead.
Export Personnel
/v1/personnel/export.csvPer-rowReturns a CSV. Accepts the same filters as List Personnel. Max 2,000 rows per export.
curl -H "Authorization: Bearer ss_live_..." \
-o contacts.csv \
"https://api.hillwinds.ai/v1/personnel/export.csv?lead_type=company&states=CA&seniority=VP-Director&has_email=true"Autocomplete
/v1/personnel/autocomplete0 creditsTypeahead suggestions. Full cross-resource reference + per-resource matrix on the Autocomplete reference. Personnel-specific notes:
column=allowlist:full_name(default),title. Other values (first_name,last_name,email_address,company_name) return400.queryrequires ≥ 2 characters; missing or 1-char →400. Alias:search.lead_type=company|brokeris accepted to restrict results to one side. The autocomplete endpoint does not require it (unlike the list endpoint) but passing it produces sharper suggestions and is the right call when you're feeding the result back into a/v1/personnel?lead_type=…&search=…follow-up.idis returned as""today. For name → ID, use the list endpoint with?search=+ a narrowing filter (lead_type,company_eins, etc.).
curl -H "Authorization: Bearer ss_live_..." \
"https://api.hillwinds.ai/v1/personnel/autocomplete?search=smith&limit=5"{
"ok": true,
"data": [
{
"id": "",
"label": "Smitha Allapat",
"resource": "personnel",
"value": "Smitha Allapat"
}
],
"meta": { "credits_charged": 0, "returned": 1 }
}Keys are issued by our team, not a signup form. Book a 25-minute walkthrough and you'll leave with sandbox and live credentials.