Skip to main content
POST
Hiring Manager Contacts API
Requires the Hiring Manager API add-on. It is pay as you go with no monthly fee and works with any plan, including Free. Add it in the web app under Settings → Subscriptions. Keys without it receive 403 with X-Billing-Code: feature_missing.
Pricing. Settled when the task finishes, from what was actually found:A search that finds no one, or fails, costs nothing; so does one where your functions / relationships filters and exclude list leave no one, and so does a reveal that finds nothing. Submitting the same job_id again within 24 hours of a charged search is not charged again (reveals you ask for on that call still are). Usage is charged when your unbilled usage reaches the current threshold or at the end of your billing month, whichever comes first. The threshold starts at $10 and rises to $50, then $100, after each successful charge. Web-app (dashboard) usage is not metered. See Usage Headers & Metering.
Find the people likely behind a specific job posting: name, role, and LinkedIn profile, plus a work email or phone number when you ask for them with reveal_email / reveal_phone. Each contact says how they relate to the role (relationship), and contacts come ranked: the hiring manager first, then the function’s leaders, then recruiters and HR. Submit a job_id, then poll the Task Status endpoint until the extraction finishes. To queue many postings at once, use Batch Submit (up to 100 per request) and poll them together with List Tasks.
This is an asynchronous, agent-powered endpoint. Extraction runs web research in the background and can take anywhere from a few seconds to several minutes. Poll the task rather than blocking on a single request.

Endpoint

Authentication

string
required
Your Hirebase API key. Required on every request — this is an always-authenticated, paid-feature endpoint.

Request Body

string
required
The job’s MongoDB ObjectId (_id from any Search Jobs result). Postings that have since expired are accepted: one that expired in the last 30 days is researched like a live one, and an older one returns contacts only if they were researched before (otherwise the task fails with job_expired). An id that matches no posting at all is rejected with 404 before any task is created.
string[]
Only return people in these functions: legal, hr, recruiting, executive, other. Each contact’s function is classified from its role title and returned as function. For example, ["legal", "hr", "recruiting"] drops COOs, claims managers and other roles outside those three. If nobody is left, the task fails with no_matching_contacts and is not charged.
string[]
Only return people with these relationships to the role: hiring_manager, function_leader, recruiter, hr, executive, other (see the relationship field below). For example, ["hiring_manager", "function_leader"] returns only the person the role reports to and the leaders of its team, without recruiters or HR. Combines with functions: a contact must match both. If nobody is left, the task fails with no_matching_contacts and is not charged.
object
People you already have, so they are not returned and never cost a reveal. Up to 10,000 entries in total. Excluded people are dropped before any email or phone lookup; if an email revealed for someone is on your list, they are dropped too and that reveal is not charged.
boolean
default:"false"
Also look up a work email for every contact found. $0.05 per email actually found (`m_contact_email_reveals`), on top of the $0.30 search.
boolean
default:"false"
Also look up a direct phone number (mobile preferred) for every contact found. $0.40 per number actually found (m_contact_phone_reveals). Adds a minute or two to the task. Personal emails and numbers are never returned.
To reveal details for a single contact you already have, use Reveal Contact instead.

Response

Returns a task object immediately (the extraction runs asynchronously). Use the returned id to poll GET /v2/tasks/{task_id}. When state becomes "finished", result.contacts is populated.
string
Unique task ID (UUID). Use it to poll task status.
string
Always "contacts_extraction" for this endpoint.
string
Task state: queued, processing, finished, or failed.
number | null
Progress as a float between 0.0 and 1.0 (may be null).
string
ID of the user who initiated the task.
object
Echo of the request body, e.g. { "job_id": "..." }.
string
ISO 8601 timestamp of task creation.
string
ISO 8601 timestamp of last update.
string | null
ISO 8601 timestamp when processing began, or null.
string | null
ISO 8601 timestamp when the task finished, or null.
string | null
Error message when state is "failed", otherwise null.
string | null
Machine-readable reason when state is "failed", otherwise null. See Why a task failed.
boolean | null
When state is "failed": true if submitting the same job_id again can succeed, false if it won’t. null otherwise.
object | null
Present only when state is "finished".

Example

1. Start the extraction

Response — task created

2. Poll for the result

Response — finished
Timing: a posting researched for the first time takes a few minutes (about 8 minutes at the median); one already researched returns in seconds. Build for minute-scale latency, not seconds. Coverage: email and photo_url are often null — name, role, and profile_url (LinkedIn) are the most reliably populated fields.

Why a task failed

A failed task (state: "failed") is never charged. Use error_code to decide what to do next; error carries the same reason in words.
Response — failed

Error Responses

Missing or invalid API key. Body: { "detail": "Sign in to access this endpoint" } (missing key) or { "detail": "Invalid API key" }.
Your account doesn’t have the Hiring Manager API add-on. Body: { "detail": "Your current plan does not include access to this endpoint. Upgrade at /v2/billing/portal." } with header X-Billing-Code: feature_missing. Add it under Settings → Subscriptions.
Your last Hiring Manager API charge failed. Body: { "detail": "Your last Hiring Manager API payment failed. Update your payment method at /v2/billing/portal to continue; tasks already queued keep running.", "code": "payment_failed" }. Update your payment method at /v2/billing/portal (or in the web app under Settings → Subscriptions); requests work again as soon as the payment goes through. Tasks you already queued keep running. Only a failed charge causes this, never unbilled usage.
No live or archived posting has this job_id. Body: { "detail": "No live or archived posting has the id 'abc'. Use the _id of a Hirebase job.", "code": "job_not_found" }. No task is created and nothing is charged.
Missing or malformed job_id. Body: { "detail": [ { "type": "missing", "loc": ["body","job_id"], "msg": "Field required", "input": {} } ] }.
The research itself can still fail after the task is created. Always check the task state after polling, and use error_code and retryable to decide whether to submit again. See Why a task failed.

Notes & Best Practices

  • Poll, don’t block. Extraction typically takes seconds to a few minutes. Poll GET /v2/tasks/{id} every few seconds, or one List Tasks call for a whole batch; treat finished and failed as terminal. Task reads have their own budget of 600 requests per minute, so polling never uses up the 100 per minute your searches and submits share.
  • Contacts are best-effort. profile_url, email, and photo_url may be null when not found; contacts may be empty for some postings.
  • The task object is the same shape used by Export Jobs — only type (contacts_extraction) and result differ.