> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/sdk/javascript/lists-and-bulk
> 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

# Lists and Bulk enrichment

> Create persistent Lists or enrich up to 25,000 people with the TypeScript SDK, follow import operations and Bulk jobs, and download CSV results.

- Last verified: 2026-11-04

Save people in a List for ongoing work, or run a Bulk job when you need a results file.

## Enrich people in bulk

From version 0.2.0, `client.lists` manages your [Lists](https://docs.reversecontact.com/guides/lists) and `client.bulk` runs [Bulk enrichment](https://docs.reversecontact.com/endpoints/bulk-enrich-persons) jobs.

Use `bulk.enrichPersons` to start a job of up to 25,000 people. Then call `bulk.wait` to wait until the job is `completed`, `cancelled`, `failed` or `paused`.

Once the job is completed or cancelled, `bulk.results` returns the results CSV as text without a byte order mark. Prepend `"\uFEFF"` when you save the file so a spreadsheet opens accented names correctly.

The SDK sends an `Idempotency-Key` with each new job and generates one if you don't supply it. The SDK reuses that key on its retries, so a retried request never creates a second job.

**Bulk enrichment**

```typescript [TypeScript SDK]
import { writeFile } from "node:fs/promises";
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

const created = await client.bulk.enrichPersons({
  name: "CRM export, November",
  items: [
    { linkedinUrl: "https://social.com/in/janedoe", externalId: "crm-001" },
    { firstName: "John", lastName: "Smith", companyDomain: "acme.com", externalId: "crm-002" }
  ],
  families: ["profile", "contact_email"]
});
const jobId = created.data.job.id;

const job = await client.bulk.wait(jobId);
if (job.data.links.results) {
  await writeFile("results.csv", "\uFEFF" + (await client.bulk.results(jobId)));
}
console.log(job.data.status, "credits:", job.data.credits.used);
```

### Read the results as they come

From version 0.3.0, you can use the results before the job settles. `bulk.iterateRows` yields them one at a time, in the order sent, as soon as each one is ready. While the next item runs, it waits out the `Retry-After` of the API. It ends with the job, or when the job is paused: read on later with `bulk.rows` and the last cursor. A cancelled job first finishes the items it had started, within the hour. `bulk.rows` reads one page (`cursor`, `limit` up to 100), with the job status in `extras.status`. Each item is a `JobResultLine`, a type the SDK exports. `bulk.results(jobId, { format: "ndjson" })` returns the results file as NDJSON, one `JobResultLine` per line. The [client.bulk reference](https://docs.reversecontact.com/sdk/javascript/bulk) documents each method.

**Results as they come**

```typescript [TypeScript]
for await (const item of client.bulk.iterateRows(jobId)) {
  console.log(item.row, item.input.externalId, item.status, item.creditsConsumed);
}

// Or the whole file once the job settles, one JSON object per line:
const ndjson = await client.bulk.results(jobId, { format: "ndjson" });
const items = ndjson.split("\n").filter(Boolean).map((line) => JSON.parse(line));
```

## Keep members in a List

A List keeps its members. Call `lists.importSignals` to start an import, then `lists.operations.wait` to wait until `clientAction` is `done`, `resume` or `reconcile`. The wait method returns the operation so your code can decide what to do next.

If you don't supply them, `lists.create` generates an `Idempotency-Key` and `lists.importSignals` generates an `operationId`. Pass your own `operationId` to resume a paused import after a restart.

**Lists**

```typescript [TypeScript SDK]
const list = await client.lists.create({ name: "Prospects", entity: "persons" });
const listId = list.data.id;

const imported = await client.lists.importSignals(listId, {
  signals: [{ email: "jane@acme.com", externalId: "crm-001" }],
  families: ["contact_email"],
  maxCredits: 100
});
const operation = await client.lists.operations.wait(listId, imported.data.operationId);
console.log(operation.data.clientAction, operation.data.nextStep);

for await (const member of client.lists.members.listAll(listId)) console.log(member);
const csv = await client.lists.export(listId);
```

`lists.listAll`, `lists.members.listAll` and `bulk.listAll` read every page for you. They are free, so they need no `maxResults`.

## Choose the right wait method

`client.bulk.wait(jobId)` returns when the job is `completed`, `cancelled`, `failed` or `paused`. Inspect the returned state before downloading results. `client.lists.operations.wait(listId, operationId)` returns when `clientAction` is `done`, `resume` or `reconcile`.

Neither method raises `JobFailedError` just because the operation stops unsuccessfully. Both can raise `JobTimeoutError` when the wait deadline passes.

List imports and Bulk jobs do not return the `Job` object used by live fetch and contact lookups. See [client.lists](https://docs.reversecontact.com/sdk/javascript/lists), [client.bulk](https://docs.reversecontact.com/sdk/javascript/bulk) and [Timeouts and cancellation](https://docs.reversecontact.com/sdk/javascript/timeouts) for the signatures and options.

Return to the [TypeScript SDK overview](https://docs.reversecontact.com/sdk/javascript).

## FAQ

### When should I use a List instead of Bulk enrichment?

A List keeps its members for ongoing work. Bulk enrichment processes up to 25,000 people and returns a results CSV.

### Does bulk.wait throw when a Bulk job fails?

`client.bulk.wait()` returns the job when the status is `completed`, `cancelled`, `failed` or `paused`. Inspect `job.data.status` and `job.data.links.results` before downloading a CSV.

### How do I resume a paused List import after restarting?

Pass your saved `operationId` to `client.lists.importSignals()` and follow the returned operation. Inspect `clientAction` and `nextStep` before deciding to resume.

### How do I preserve accented names in a Bulk results CSV?

`client.bulk.results()` returns CSV text without the byte order mark. Prepend `"\uFEFF"` when saving the text for spreadsheet applications.
