---
title: "Get a contact lookup"
method: GET
path: "/v1/contacts/jobs/{job_id}"
tags: ["Contacts"]
---

# Get a contact lookup

`GET /v1/contacts/jobs/{job_id}`

## Request

- Base URL: `https://api.recruitmcp.com`
- URL: `https://api.recruitmcp.com/v1/contacts/jobs/{job_id}`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://recruitmcp.com/oauth/authorize`, token URL `https://recruitmcp.com/oauth/token`, scopes: `contacts:spend`)

Free. Poll the job_id from a contact lookup that returned HTTP 202, every few seconds, until status is completed. Requires contacts:spend. status is pending until every person is stored, then completed with the emails, phones, best_email, and best_phone. cost.credits, credits_charged, and each person's credits are the credits that job charged, read from the ledger. While the job is pending they are all 0. charged_this_request is 0 while pending; once completed it is the same number as credits_charged, because the lookup that started the job is charged when the job completes. The poll itself never charges: X-Credits-Charged is 0 and polling again charges nothing more. An idempotent replay of the original lookup has charged_this_request 0 and cost.credits 0.

Polling example:
```bash
KEY="<your API key>"
curl -s -X POST https://api.recruitmcp.com/v1/people/contacts:fetch \
  -H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{"profile_ids":["lead_8f2a1c_1"]}'
# 202: {"status":"pending","job_id":"cjob_01J0CJOB0EXAMPLE","poll_url":"/v1/contacts/jobs/cjob_01J0CJOB0EXAMPLE",...}
# Poll until status is completed (pending until then). The wait doubles from 2 s up to 30 s; stop on an error, give up after about 2 minutes.
delay=2
for i in 1 2 3 4 5 6 7 8; do
  body=$(curl -sS https://api.recruitmcp.com/v1/contacts/jobs/cjob_01J0CJOB0EXAMPLE -H "Authorization: Bearer $KEY")
  echo "$body" | grep -q '"status":"completed"' && break
  echo "$body" | grep -q '"error":{' && break
  sleep $delay
  delay=$((delay*2>30?30:delay*2))
done
echo "$body"
# data[].contacts.best_email and best_phone; credits_charged and charged_this_request equal cost.credits
```

Scope: `contacts:spend`.

## Path parameters

- `job_id` string, required

## Headers

- `RecruitMCP-Version` '2026-10-13'
- `RecruitMCP-Customer` string

## Response `200`

The lookup, pending or completed.

- ContactsLookup
  - `data` Contacts[], required
    - `profile_id` string, required
    - `canonical_id` string: Search id for this person when both profile ids are known.
    - `contacts` object, required
      - `status` 'ready' | 'not_found' | 'pending' | 'expired', required
      - `emails` object[], required
        - `value` string
        - `type` 'personal' | 'work', nullable: work or personal when the lookup provides it. Null when it does not.
        - `line` 'mobile' | 'work_phone' | 'work' | 'personal': Provider line. mobile and work_phone stay on phones.
        - `confidence` number, nullable
        - `flagged` boolean: True when a plausibility check says this entry is clearly not this person.
        - `flag_reason` string: Short reason, at most 12 words. Present when flagged is true.
      - `phones` object[], required
        - `value` string
        - `type` 'personal' | 'work'
        - `line` 'mobile' | 'work_phone' | 'work' | 'personal': Provider line. mobile and work_phone stay on phones.
        - `confidence` number, nullable
        - `flagged` boolean: True when a plausibility check says this entry is clearly not this person.
        - `flag_reason` string: Short reason, at most 12 words. Present when flagged is true.
      - `best_email` object, nullable, required
        - `value` string, required
        - `type` 'personal' | 'work', nullable, required: work or personal when the lookup provides it. Null when it does not.
        - `line` 'mobile' | 'work_phone' | 'work' | 'personal': Provider line. mobile and work_phone stay on phones.
        - `confidence` number, nullable, required
        - `reason` 'only_candidate' | 'mobile' | 'highest_rating' | 'personal_preferred' | 'work_preferred' | 'matches_employer_domain' | 'llm_plausibility', required: Why this address or number was chosen. llm_plausibility means a flagged entry changed the pick.
      - `best_phone` object, nullable, required
        - `value` string, required
        - `type` 'personal' | 'work', nullable, required: work or personal when the lookup provides it. Null when it does not.
        - `line` 'mobile' | 'work_phone' | 'work' | 'personal': Provider line. mobile and work_phone stay on phones.
        - `confidence` number, nullable, required
        - `reason` 'only_candidate' | 'mobile' | 'highest_rating' | 'personal_preferred' | 'work_preferred' | 'matches_employer_domain' | 'llm_plausibility', required: Why this address or number was chosen. llm_plausibility means a flagged entry changed the pick.
      - `expires_at` string, date-time, nullable
    - `credits` number: Credits charged for this person on the lookup. 0 on a miss, a cache hit, a pending lookup, and when this response did not itself charge.
    - `cost` Cost
      - `credits` integer, required: Credits charged by this call. Whole credits. 0 on an idempotent replay, where the body also has replayed: true and the header Idempotent-Replayed: true.
      - `cached_items` integer: Items read from cache. On a replay, the value of the first response.
      - `live_items` integer: Items fetched live by this call. 0 on a replay.
      - `upstream_calls` integer: Index calls made by this call. 0 on a replay.
  - `cost` Cost, required
    - `credits` integer, required: Credits charged by this call. Whole credits. 0 on an idempotent replay, where the body also has replayed: true and the header Idempotent-Replayed: true.
    - `cached_items` integer: Items read from cache. On a replay, the value of the first response.
    - `live_items` integer: Items fetched live by this call. 0 on a replay.
    - `upstream_calls` integer: Index calls made by this call. 0 on a replay.
  - `status` 'pending' | 'completed': pending while the lookup is running. completed when the job poll has the stored contacts.
  - `request_id` string, nullable: Id of the running lookup.
  - `job_id` string: Set on HTTP 202. Poll GET /v1/contacts/jobs/{job_id}.
  - `poll_url` string: Path of GET /v1/contacts/jobs/{job_id}. Requires contacts:spend.
  - `canonical_id` string, nullable: Search id when one person was requested.
  - `charged_this_request` number: Credits charged for the lookup this body describes. Matches cost.credits. On a 202 and on a pending poll it is 0. On a completed poll it is what the lookup that started the job was charged at completion, the same as credits_charged; the poll itself charges nothing. 0 on an idempotent replay, where cost.credits is also 0.
  - `credits_charged` number: Set on a contact job (HTTP 202 and GET /v1/contacts/jobs/{job_id}). Credits the ledger charged for this job: 0 while pending, then the same number as cost.credits.

## Other responses

- `400`: `invalid_request` (do not retry): A field is missing or has a value this endpoint does not accept. `unsupported_version` (do not retry): RecruitMCP-Version is not a supported version.
- `401`: `unauthorized` (do not retry): Missing bearer API key.
- `403`: `forbidden` (do not retry): This customer is disabled. `insufficient_scope` (do not retry): This key does not include the scope this endpoint requires.
- `404`: `not_found` (do not retry): No record with this id exists in the workspace.
- `429`: `rate_limited` (retry): Per-key rate limit exceeded.

## Changes

- **2026-10-20** `ad27b35513a6`: 5 warning
  - added the new `approval_forced` enum value to the `error/code` response property for the response status `400`
  - added the new `approval_forced` enum value to the `error/code` response property for the response status `401`
  - added the new `approval_forced` enum value to the `error/code` response property for the response status `403`
  - added the new `approval_forced` enum value to the `error/code` response property for the response status `404`
  - …1 more

[Operation history](https://skmtc.dev/recruitmcp/apis/recruitmcp-api/changes/v1/contacts/jobs/:job_id/get.md)

---

[API](https://skmtc.dev/recruitmcp/apis/recruitmcp-api.md) · [All operations](https://skmtc.dev/recruitmcp/apis/recruitmcp-api/llms.txt) · [OpenAPI document](https://skmtc.dev/recruitmcp/apis/recruitmcp-api/revisions/ad27b35513a6?raw)
