Hiring Manager API
Hiring Manager Contacts API
Extract likely hiring-manager / recruiter contacts for a specific job posting.
POST
Hiring Manager Contacts API
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.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.
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 returnedid 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
403 Forbidden
403 Forbidden
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.402 Payment Required
402 Payment Required
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.404 Not Found
404 Not Found
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.422 Unprocessable Entity
422 Unprocessable Entity
Missing or malformed
job_id. Body: { "detail": [ { "type": "missing", "loc": ["body","job_id"], "msg": "Field required", "input": {} } ] }.Task failed (state: "failed")
Task failed (state: "failed")
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; treatfinishedandfailedas 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, andphoto_urlmay benullwhen not found;contactsmay be empty for some postings. - The task object is the same shape used by Export Jobs — only
type(contacts_extraction) andresultdiffer.