Hillwinds API
API Reference

Personnel

Find decision-maker contacts with email, LinkedIn, title, and seniority data.

Before you query — three quirks worth knowing

(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

GET/v1/personnel0.50 / 2.00 per row

Returns a paginated list of personnel contacts matching your filters.

Pagination & format

Common to all endpoints — see Pagination and Response Modes.

ParameterTypeDescription
pageintegerPage number (0-indexed)
page_sizeintegerResults per page (max 500). Over-max → 400.
sort_bystringColumn to sort by — allowlist: last_name (default), company_name, total_number_of_employees
sort_dirasc | descSort direction
modebasic | fullResponse verbosity. Advanced-only fields require advanced access.
fieldsstringComma-separated field list (id always included; advanced field with basic key → 403)
formatjson | flatResponse format (flat for Clay/spreadsheets)
count_onlybooleanReturn only total count, no data rows (0 credits)
include_countbooleanInclude total_count in meta (default true; false skips per-page COUNT)
dry_runbooleanValidate query and return estimated rows + credits (0 credits)

Basic response fields

FieldTypeDescription
lead_sourceobjectAttribution 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.
tagsstring[]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

ParameterTypeDescription
searchstringFuzzy name search across full_name and title
lead_typecompany | brokerRequired. Determines whether the contact sits at a company or a broker office
senioritystringComma-separated: C-suite, VP-Director, Mid-Level, Entry-Level
job_functionstringBucketed job function category (e.g. HR, Finance, Operations)
job_titlestringFree-text title search
has_emailbooleanContacts with verified email
has_phonebooleanContacts with phone (advanced tier)
has_linkedinbooleanContacts with LinkedIn URL
form5500_rolestringComma-separated Form 5500 roles (Plan Administrator, Sponsor, etc.)
statesstringCompany-side state codes
dmasstringCompany-side DMA names
geo_modestate | dmaCompany-side geographic mode (default state)
person_statesstringState codes for the person's own location. Applies on its own — no person_geo_mode required (defaults to state).
person_dmasstringDMA names for the person's own location. Set person_geo_mode=dma to use these instead of person_states.
person_geo_modestate | dmaOptional. Only needed to pick dma when both person_states and person_dmas are supplied.
company_einsstringComma-separated company EINs (hierarchical drill-down for company personnel)
broker_office_idsstringComma-separated broker office IDs (hierarchical drill-down for broker personnel)
broker_idsstringComma-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).
industriesstringIndustry names (company personnel only)
employee_bandsstringCompany employee bands (company personnel only)
funding_statusSelf-Funded | Fully InsuredCompany funding status (company personnel only)
renewal_monthstringCompany renewal month (company personnel only)
filing_yearstringFiling year or 'latest' (default 'latest')
short_formbooleanShort-form filing flag (company personnel only)
long_tailbooleanLong-tail company flag (company personnel only)
long_tail_brokersbooleanLong-tail broker flag (broker personnel only)
signalsstringActive signals on the parent entity (advanced tier only)
signal_matchany | allMatch semantics when multiple signals= are passed (default any)
brokerstringBroker name on the parent entity (substring match, company personnel only)
broker_officestringBroker office name on the parent entity (company personnel only)
carrierstringCarrier on the parent entity (company personnel only)
benefit_typestringInsurance line on the parent entity (company personnel only)
tagsstringTag names (AND logic)
crm_statusstringin_hubspot | in_salesforce | in_both | not_in_crm
cf[column]stringColumn filter — supports gte:/lte: for numeric columns
eb[column]booleanExclude blanks for column; only true is supported (false returns 400)
lead_type is required
Every personnel request must include 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

bash
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

POST/v1/personnel/batch0.50 / 2.00 per matched row

Find 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.

ParameterTypeDescription
company_einsstring[]EINs of companies to enrich. Provide this or ids.
idsstring[]Personnel IDs to fetch directly. Provide this or company_eins.
lead_typerequiredcompany | brokerRequired. Accepted in the body OR as a query string — same rule as the list endpoint.
page_sizeintegerMax 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.

bash
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

GET/v1/personnel/:id0.50 / 2.00 per row

Returns 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.

ParameterTypeDescription
idrequiredstringPersonnel ID (path).
lead_typerequiredcompany | brokerMust match the side the contact sits on.
modebasic | fullField verbosity (same semantics as the list endpoint).
fieldsstringComma-separated field allowlist.
formatjson | flatResponse format.
bash
curl -H "Authorization: Bearer ss_live_..." \
  "https://api.hillwinds.ai/v1/personnel/per_a1b2c3d4?lead_type=company&mode=full"

List Tags for a Contact

GET/v1/personnel/:id/tags0 credits

Returns 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.

ParameterTypeDescription
idrequiredstringPersonnel ID (path).
bash
curl -H "Authorization: Bearer ss_live_..." \
  "https://api.hillwinds.ai/v1/personnel/per_a1b2c3d4/tags"
json
{
  "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.

bash
# 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 is still required
Even for a reverse lookup, 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[] takes the field name, not the param name
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

GET/v1/personnel/export.csvPer-row

Returns a CSV. Accepts the same filters as List Personnel. Max 2,000 rows per export.

bash
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

GET/v1/personnel/autocomplete0 credits

Typeahead 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) return 400.
  • query requires ≥ 2 characters; missing or 1-char → 400. Alias: search.
  • lead_type=company|broker is 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.
  • id is returned as "" today. For name → ID, use the list endpoint with ?search= + a narrowing filter (lead_type, company_eins, etc.).
bash
curl -H "Authorization: Bearer ss_live_..." \
  "https://api.hillwinds.ai/v1/personnel/autocomplete?search=smith&limit=5"
json
{
  "ok": true,
  "data": [
    {
      "id": "",
      "label": "Smitha Allapat",
      "resource": "personnel",
      "value": "Smitha Allapat"
    }
  ],
  "meta": { "credits_charged": 0, "returned": 1 }
}
Need a key?

Keys are issued by our team, not a signup form. Book a 25-minute walkthrough and you'll leave with sandbox and live credentials.