> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/endpoints/job
> Every page is listed at https://docs.reversecontact.com/llms.txt, every endpoint with its credit cost and status at https://docs.reversecontact.com/catalog.json

# Get a job

> Follow a job with long polling until it settles, with its progress, the credits it reserved and spent, and the link to its results, for free.

- Endpoint: `GET https://api.reversecontact.com/v2/jobs/:jobId`
- Credits: Free, also at a zero balance, and never counted against the rate limits.
- Answer: synchronous.
- Available in: REST API, MCP tool `get_job`, TypeScript and Python SDKs, CLI command `rc bulk get`.
- Last verified: 2026-11-04

## Introduction

The Get a job endpoint reads one job: it is the `links.self` path of a job you [created](https://docs.reversecontact.com/endpoints/bulk-enrich-persons). It says how far the job is, what it reserved and spent, and, once it is completed or cancelled, where to download its results. It reads any job of the [job history](https://docs.reversecontact.com/endpoints/jobs) too, such as a List import or a Search export started in the app.

Reading a job is **free**, also at a zero balance, and never counted against your rate limits, so you can follow a long job at the pace it asks for. A key reads the jobs its permissions allow: Person Enrichment or Contact for a [Bulk enrichment](https://docs.reversecontact.com/endpoints/bulk-enrich-persons) job, Lists for a List job, Search for a Search export. A job the key may not read returns `404`, as a job that does not exist.

## Authorization

| Name     | Type   | Required | Description                                                                                         |
| -------- | ------ | -------- | --------------------------------------------------------------------------------------------------- |
| `apikey` | string | Yes      | Your API key from the developer dashboard. Pass it via `Authorization: Bearer YOUR_API_KEY` header. |

## Request

The route is `GET /v2/jobs/:jobId`.

### Path parameters

| Name    | Type   | Required | Description                |
| ------- | ------ | -------- | -------------------------- |
| `jobId` | string | Yes      | The `id` of the job (UUID) |

### Query Parameters

| Name   | Type    | Required | Description                                                                                                                                                                                              |
| ------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `wait` | integer | No       | Hold the request up to this many seconds, 0 to 20, while the job is queued or running. It answers as soon as the job settles or pauses, or with the job still running when the wait ends. Defaults to 0. |

### How to follow a job

Read the job while its `status` is `queued` or `running`, with `wait=20` and an HTTP client timeout above it, for example 30 seconds. While the job moves, the response carries a `Retry-After: 2` header: start two reads at least that far apart. A held read already lasts longer, so the next one starts right away.

| `status`    | What it means                                                                                                                      |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `queued`    | Waiting for its turn: read it again                                                                                                |
| `running`   | Items are being looked up: read it again                                                                                           |
| `paused`    | It waits to be resumed: a member of your workspace paused it in the app, or a List import reached its `maxCredits` or your balance |
| `completed` | Every item was handled: download the results at `links.results`                                                                    |
| `cancelled` | Stopped: the items not started were never charged. The results of the items done are at `links.results`, where an item still in flight reads `running` until it ends, within the hour |
| `failed`    | The job could not run: `error.code` says why, and it has no results file                                                           |

`completed`, `cancelled` and `failed` are final. The [webhook](https://docs.reversecontact.com/endpoints/bulk-enrich-persons#webhook-callback) of a job is sent when it reaches one of them.

## Response structure

| Field     | Type    | Description                                      |
| --------- | ------- | ------------------------------------------------ |
| `success` | boolean | `true` if the request was processed successfully |
| `error`   | `null`  | Always `null` on success                         |

**data** (Job): The job

| Field                       | Type           | Description                                                                                                                                                                                     |
| --------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                        | string         | Job id (UUID)                                                                                                                                                                                   |
| `type`                      | string         | `enrich_persons` (Bulk enrichment, or a file enriched in the app), `list_import`, `list_enrich`, `list_search_import` or `search_export`, see [the job history](https://docs.reversecontact.com/endpoints/jobs#job-types) |
| `entity`                    | string         | `persons` or `companies`                                                                                                                                                                        |
| `status`                    | string         | `queued`, `running`, `paused`, `completed`, `cancelled` or `failed`                                                                                                                             |
| `source`                    | string \| null | Who started it: `api` (an API key), `mcp` (an AI assistant), `ask` or `dashboard` (a person in the app). `null` for a job older than this field.                                                |
| `name`                      | string         | The `name` given at creation, or the file name of a job started in the app                                                                                                                      |
| `listId`                    | string \| null | The List a List job works on, `null` otherwise                                                                                                                                                  |
| `estimatedSecondsRemaining` | number \| null | Seconds left at the recent pace while the job runs, `null` when unknown or settled                                                                                                              |
| `error`                     | object \| null | `{ "code": "..." }` when the job failed, `null` otherwise                                                                                                                                       |
| `createdAt`                 | string         | Creation date (ISO 8601)                                                                                                                                                                        |
| `startedAt`                 | string \| null | When it started running, `null` while queued                                                                                                                                                    |
| `completedAt`               | string \| null | When it settled, `null` while it moves                                                                                                                                                          |

**progress** (JobProgress): How far the job is

| Field       | Type   | Description                                                                   |
| ----------- | ------ | ----------------------------------------------------------------------------- |
| `total`     | number | Items in the job, duplicates included                                         |
| `processed` | number | Items done                                                                    |
| `succeeded` | number | Items that found at least part of what was asked                              |
| `failed`    | number | Items that found nothing, or could not be looked up                           |
| `skipped`   | number | Items not run: a duplicate, no usable field, an invalid profile URL, not enough credits, or cancelled |
| `remaining` | number | Items left to do                                                              |

**credits** (JobCredits): What the job reserved and spent

| Field       | Type           | Description                                                                                  |
| ----------- | -------------- | -------------------------------------------------------------------------------------------- |
| `estimated` | number         | Credits reserved when the job was created, `0` for a job that reserves nothing up front, such as a List import |
| `used`      | number \| null | Credits spent. A List import counts them as it goes; any other job reads `null` until it has settled them, then gives back the rest of `estimated`. |

**links** (JobLinks): Where to go next

| Field       | Type           | Description                                                                                                                                   |
| ----------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `self`      | string         | Path that reads this job                                                                                                                      |
| `results`   | string \| null | Path of the results file, CSV or NDJSON, set once an `enrich_persons` job is completed or cancelled. See [Download job results](https://docs.reversecontact.com/endpoints/job-results). To read the results while the job runs, see [Read job results by page](https://docs.reversecontact.com/endpoints/job-rows). |
| `operation` | string \| null | The [List operation](https://docs.reversecontact.com/endpoints/list-operation) a List job runs as, `null` for other jobs                                                |

`quotas` and `metadata` are the blocks of every V2 response: see [Usage](https://docs.reversecontact.com/endpoints/usage#response-structure).

## Errors

| Status | Code               | Meaning                                                             |
| ------ | ------------------ | ------------------------------------------------------------------- |
| `404`  | `JOB_NOT_FOUND`    | No job with this id in your workspace, or one your key may not read |
| `422`  | `VALIDATION_ERROR` | `jobId` is not a UUID, or `wait` is not an integer from 0 to 20     |

## Code examples

**Code Examples**

```bash [cURL]
curl "https://api.reversecontact.com/v2/jobs/7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60?wait=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```
```javascript [JavaScript]
const jobId = "7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60";
const headers = { "Authorization": "Bearer YOUR_API_KEY" };

let job;
do {
  const response = await fetch(`https://api.reversecontact.com/v2/jobs/${jobId}?wait=20`, { headers });
  job = (await response.json()).data;
} while (job.status === "queued" || job.status === "running");

console.log(job.status, `${job.credits.used} of ${job.credits.estimated} credits`, job.links.results);
```
```python [Python]
import requests

job_id = "7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60"
headers = {"Authorization": "Bearer YOUR_API_KEY"}

while True:
    job = requests.get(
        f"https://api.reversecontact.com/v2/jobs/{job_id}",
        headers=headers,
        params={"wait": 20},
        timeout=30
    ).json()["data"]
    if job["status"] not in ("queued", "running"):
        break

print(job["status"], job["credits"]["used"], job["links"]["results"])
```

## Example response

**Example Response**

```json [200 - Completed]
{
  "success": true,
  "data": {
    "id": "7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60",
    "type": "enrich_persons",
    "entity": "persons",
    "status": "completed",
    "source": "api",
    "name": "CRM export, November",
    "listId": null,
    "progress": {
      "total": 2,
      "processed": 2,
      "succeeded": 2,
      "failed": 0,
      "skipped": 0,
      "remaining": 0
    },
    "credits": { "estimated": 9, "used": 6 },
    "estimatedSecondsRemaining": null,
    "error": null,
    "createdAt": "2026-11-04T09:30:00.000Z",
    "startedAt": "2026-11-04T09:30:01.000Z",
    "completedAt": "2026-11-04T09:31:12.000Z",
    "links": {
      "self": "/v2/jobs/7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60",
      "results": "/v2/jobs/7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60/results",
      "operation": null
    }
  },
  "error": null,
  "quotas": {
    "creditsConsumed": 0,
    "workspace": {
      "id": "ws_abc123",
      "credits": { "total": 10000, "used": 4528, "left": 5472 }
    }
  },
  "metadata": {
    "requestId": "e5e6c816-df62-43a7-8fbe-1d3a94b6909c",
    "executionTimeMs": 31
  }
}
```
```json [404 - Not found]
{
  "success": false,
  "data": null,
  "error": {
    "code": "JOB_NOT_FOUND",
    "message": "No job found with that identifier"
  },
  "quotas": {
    "creditsConsumed": 0,
    "workspace": {
      "id": "ws_abc123",
      "credits": { "total": 10000, "used": 4528, "left": 5472 }
    }
  },
  "metadata": {
    "requestId": "6408b12a-8668-464f-97f3-3a1497efb4f5",
    "executionTimeMs": 12
  }
}
```
