Skip to content

MaintenanceThe former company search endpoint goes into maintenance on November 9Migration guide

SDKs // Python // Resources

client.bulk

Last verified

Start, monitor and cancel Bulk enrichment jobs with the Python SDK, read results as they come, or download them as CSV or NDJSON.

Start Bulk enrichment jobs, follow progress, browse job history, read results as they come and download them.

MethodEndpointCost
enrich_persons()POST /v2/enrich/persons/bulkFree to send. Each item costs the same as its individual call. Credits are reserved when the job is created and refunded for what is not found.
list()GET /v2/jobsFree.
list_all()GET /v2/jobsFree.
retrieve()GET /v2/jobs/{jobId}Free, even at a zero balance. Never counts toward rate limits.
results()GET /v2/jobs/{jobId}/resultsFree, even at a zero balance.
rows()GET /v2/jobs/{jobId}/rowsFree, even at a zero balance. Each page counts toward rate limits.
iter_rows()GET /v2/jobs/{jobId}/rowsFree, even at a zero balance. Each page counts toward rate limits.
cancel()POST /v2/jobs/{jobId}/cancelFree. Items that had not started are never charged.
wait()GET /v2/jobs/{jobId}Free, even at a zero balance. Never counts toward rate limits.

Calling conventions

Examples assume RC_API_KEY is set. Replace example ids with ids returned by your own requests. Signatures show the published 0.3.0 synchronous methods. Use the same arguments with the Python async client.

models refers to reversecontact.types. NOT_GIVEN and NotGiven are the SDK's internal markers for omitted arguments. Leave these arguments out of your calls. For API body parameters, passing None sends an explicit null. Transport options use None to inherit their defaults. Timeout means a number of seconds or httpx.Timeout. Supply parameters marked Required to Python or to the API, even when the generated signature permits omission.

JSON response objects expose data, quotas, metadata and http_response. Check optional fields for None. See First call for the response envelope. CSV methods return text and iterators yield items.

HTTP failures raise an APIError subclass based on the response: BadRequestError, AuthenticationError, InsufficientCreditsError, PermissionDeniedError, NotFoundError, ConflictError, GoneError, UnprocessableEntityError, RateLimitError, DailyLimitError, KeyCreditLimitError, UnavailableForLegalReasonsError or InternalServerError. See the error mapping and retry rules.

enrich_persons

Send 1 to 25,000 items. Nested item dictionaries use the API names, such as linkedinUrl, firstName and externalId.

Python SDK

Python SDK

def enrich_persons(
    *,
    items: list[models.BulkEnrichPersonsParamsItemsItem | dict[str, object]] | NotGiven | None = NOT_GIVEN,
    families: list[str] | NotGiven | None = NOT_GIVEN,
    email_type: str | NotGiven | None = NOT_GIVEN,
    live: bool | NotGiven | None = NOT_GIVEN,
    name: str | NotGiven | None = NOT_GIVEN,
    webhook_url: str | NotGiven | None = NOT_GIVEN,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    idempotency_key: str | None = None,
    **extra: object,
) -> Result[models.BulkEnrichPersonsData]: ...

Parameters

NameTypeDescription
itemslist[models.BulkEnrichPersonsParamsItemsItem | dict[str, object]] | NotGiven | NoneRequired. 1 to 25,000 people, see Items
familieslist[str] | NotGiven | NoneOptional. What to look up for each item: profile, contact_email, contact_phone. Defaults to ["profile"].
email_typestr | NotGiven | NoneOptional. With contact_email, the kind of email to find: professional (default), personal or any
livebool | NotGiven | NoneOptional. true fetches the profile of a Social profile URL item live. Omitted or false, saved data is used.
namestr | NotGiven | NoneOptional. A name for the job, up to 255 characters, shown in the job history and in the app. Defaults to Sent via API.
webhook_urlstr | NotGiven | NoneOptional. Public HTTPS URL that receives one POST when the job settles. See Webhook callback
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
idempotency_keystr | NoneOptional. Key for this creation request. The SDK generates a key when omitted and reuses it on automatic retries.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

Result[models.BulkEnrichPersonsData]. data.job.id identifies the Bulk job. The acknowledgement also contains duplicate_count, credits_estimated and not_run_for_credits. Follow the job with client.bulk.wait(job_id). See Lists and Bulk enrichment.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options. Malformed typed response data can raise Pydantic ValidationError.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    result = client.bulk.enrich_persons(
        items=[{"linkedinUrl": "https://social.com/in/janedoe", "externalId": "crm-001"}],
        families=["profile"],
        name="CRM enrichment",
    )
    print(result.data if result is not None else None)

list

Call Job history with client.bulk.list().

Python SDK

Python SDK

def list(
    *,
    type: str | NotGiven | None = NOT_GIVEN,
    status: str | NotGiven | None = NOT_GIVEN,
    list_id: str | NotGiven | None = NOT_GIVEN,
    per_page: int | NotGiven | None = NOT_GIVEN,
    cursor: str | NotGiven | None = NOT_GIVEN,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    **extra: object,
) -> Page[models.ListJobsItem]: ...

Parameters

NameTypeDescription
typestr | NotGiven | NoneOptional. Filter by these comma-separated types. See Job types
statusstr | NotGiven | NoneOptional. Filter by these comma-separated statuses: queued, running, paused, completed, cancelled, failed
list_idstr | NotGiven | NoneOptional. Return only jobs for this List (UUID)
per_pageint | NotGiven | NoneOptional. Jobs per page, from 1 to 100. Defaults to 50.
cursorstr | NotGiven | NoneOptional. The nextCursor of the previous page, sent with the same filters. A malformed cursor, or one sent with other filters, returns 400.
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

Page[models.ListJobsItem]. A page with records in data, a next_cursor, has_next_page() and get_next_page(). The next-page method reuses the filters and returns None after the last page.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options. Malformed typed response data can raise Pydantic ValidationError.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    page = client.bulk.list(per_page=20)
    for item in page.data:
        if item is not None:
            print(item.to_dict())

list_all

Read every job-history page with client.bulk.list_all(per_page=100). In SDK 0.3.0, omitting per_page starts the iterator at a page size of 200, reduced by any remaining max_results cap. The API accepts at most 100 per page, so set per_page explicitly to a value from 1 to 100.

Python SDK

Python SDK

def list_all(
    *,
    type: str | NotGiven | None = NOT_GIVEN,
    status: str | NotGiven | None = NOT_GIVEN,
    list_id: str | NotGiven | None = NOT_GIVEN,
    per_page: int | NotGiven | None = NOT_GIVEN,
    cursor: str | NotGiven | None = NOT_GIVEN,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    max_results: int | None = None,
    **extra: object,
) -> Iterator[models.ListJobsItem | None]: ...

Parameters

NameTypeDescription
typestr | NotGiven | NoneOptional. Filter by these comma-separated types. See Job types
statusstr | NotGiven | NoneOptional. Filter by these comma-separated statuses: queued, running, paused, completed, cancelled, failed
list_idstr | NotGiven | NoneOptional. Return only jobs for this List (UUID)
per_pageint | NotGiven | NoneOptional in the signature. Set 1 to 100 explicitly: SDK 0.3.0 otherwise starts from 200 before applying max_results, above the API maximum of 100.
cursorstr | NotGiven | NoneOptional. The nextCursor of the previous page, sent with the same filters. A malformed cursor, or one sent with other filters, returns 400.
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
max_resultsint | NoneOptional. Maximum items yielded. None reads every page. 0 makes no request.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

Iterator[models.ListJobsItem | None]. An iterator of typed records. Entries can be None. The iterator fetches pages only as you advance. Pagination is free. Omit max_results to read all pages.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options, invalid max_results or per_page, or a repeated pagination cursor. Malformed typed response data can raise Pydantic ValidationError.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    for item in client.bulk.list_all(per_page=100, max_results=20):
        if item is not None:
            print(item.to_dict())

retrieve

Call Get a job with client.bulk.retrieve().

Python SDK

Python SDK

def retrieve(
    job_id: str,
    *,
    wait: int | NotGiven | None = NOT_GIVEN,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    **extra: object,
) -> Result[models.GetJobData]: ...

Parameters

NameTypeDescription
job_idstrRequired. Bulk job id (UUID).
waitint | NotGiven | NoneOptional. 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.
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

Result[models.GetJobData]. data contains one job state, including status, progress, credits, error and links.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options or a wait outside the integer range 0 to 20. Malformed typed response data can raise Pydantic ValidationError.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    result = client.bulk.retrieve("00000000-0000-4000-8000-000000000001", wait=20)
    print(result.data if result is not None else None)

results

Results are available once the job is completed or cancelled, as CSV (the default) or with format="ndjson" as one JobResultLine JSON object per line.

Python SDK

Python SDK

def results(
    job_id: str,
    *,
    format: str | NotGiven | None = NOT_GIVEN,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    **extra: object,
) -> str: ...

Parameters

NameTypeDescription
job_idstrRequired. Bulk job id (UUID).
formatstr | NotGiven | NoneOptional. csv or ndjson. Defaults to csv.
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

str. The completed or cancelled job's results as text, one row per item in input order: the CSV, or NDJSON with format="ndjson".

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options. ConflictError with JOB_RESULTS_NOT_READY means the file is not ready. ConflictError with JOB_RESULTS_FORMAT_UNAVAILABLE means the job was stored before NDJSON existed: download its CSV.

Example

Python SDK

Python SDK

import json

from reversecontact import ReverseContact

with ReverseContact() as client:
    job_id = "00000000-0000-4000-8000-000000000001"
    csv_text = client.bulk.results(job_id)
    ndjson = client.bulk.results(job_id, format="ndjson")
    items = [json.loads(line) for line in ndjson.splitlines() if line]
    print(len(csv_text), len(items))

rows

Read one page of results, in the order sent, from the start of the run. While the job runs, a page stops at the first item not finished: data is empty and next_cursor is set while the next item runs, so read it again after Retry-After. next_cursor is None once nothing more will come. Available from SDK 0.3.0.

Python SDK

Python SDK

def rows(
    job_id: str,
    *,
    cursor: str | NotGiven | None = NOT_GIVEN,
    limit: int | NotGiven | None = NOT_GIVEN,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    **extra: object,
) -> Page[models.JobResultLine]: ...

Parameters

NameTypeDescription
job_idstrRequired. Bulk job id (UUID).
cursorstr | NotGiven | NoneOptional. next_cursor of the previous page, up to 512 characters. Without it, the page starts at the first item.
limitint | NotGiven | NoneOptional. Items per page, 1 to 100. Defaults to 100.
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

Page[models.JobResultLine], with the items in data, next_cursor, has_next_page() and get_next_page(). extras["status"] is the job status when the page was read. A page can hold fewer items than limit.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options. ConflictError with JOB_RESULTS_FORMAT_UNAVAILABLE means the job was stored before reading by page existed: download its CSV with results(). RateLimitError with JOB_ROWS_BUSY means too many pages are being read at once: the SDK retries it as the retry rules describe.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    page = client.bulk.rows("00000000-0000-4000-8000-000000000001", limit=100)
    for item in page.data:
        print(item.row, item.status)
    print(page.extras["status"], page.next_cursor)

iter_rows

Read the results one item at a time, in the order sent, as they become ready, whatever the job is doing. While the next item runs, the iterator waits Retry-After (2 seconds by default) and reads the same cursor again. It ends when next_cursor is None, or when the job is paused, since nothing moves until someone resumes it: read on later with rows() and the last cursor. A cancelled job still finishes the items it had started, within the hour, and the iterator waits for them too. Available from SDK 0.3.0. The async client has the same method, used with async for.

Python SDK

Python SDK

def iter_rows(
    job_id: str,
    *,
    cursor: str | None = None,
    limit: int | None = None,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
) -> Iterator[models.JobResultLine | None]: ...

Parameters

NameTypeDescription
job_idstrRequired. Bulk job id (UUID).
cursorstr | NoneOptional. Start after a page you already read.
limitint | NoneOptional. Items per page, 1 to 100.
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.

Returns

An iterator of models.JobResultLine items, in the order sent.

Errors

The errors of rows().

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    for item in client.bulk.iter_rows("00000000-0000-4000-8000-000000000001"):
        print(item.row, item.input.get("externalId"), item.status, item.credits_consumed)

cancel

Call Cancel a job with client.bulk.cancel().

Python SDK

Python SDK

def cancel(
    job_id: str,
    *,
    timeout: Timeout | None = None,
    max_retries: int | None = None,
    headers: Mapping[str, str] | None = None,
    **extra: object,
) -> Result[models.CancelJobData]: ...

Parameters

NameTypeDescription
job_idstrRequired. Bulk job id (UUID).
timeoutTimeout | NoneOptional. Seconds per HTTP attempt, or httpx.Timeout. Uses the client setting when None.
max_retriesint | NoneOptional. Retries after the first HTTP attempt. Uses the client setting when None.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.
**extraobjectOptional. Additional API keyword arguments, including camelCase aliases, passed through to the API.

Returns

Result[models.CancelJobData]. data contains the job state after cancellation, including status, progress and credits.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid SDK options. Malformed typed response data can raise Pydantic ValidationError.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    result = client.bulk.cancel("00000000-0000-4000-8000-000000000001")
    print(result.data if result is not None else None)

wait

Call Get a job with client.bulk.wait().

Python SDK

Python SDK

def wait(
    job_id: str,
    *,
    timeout: float = 1800,
    poll_wait: int = 20,
    on_poll: Callable[[Result[GetJobData]], None] | None = None,
    headers: Mapping[str, str] | None = None,
) -> Result[GetJobData]: ...

Parameters

NameTypeDescription
job_idstrRequired. Bulk job id (UUID).
timeoutfloatOptional. Overall polling deadline in seconds. Default 1800.
poll_waitintOptional. Long-poll duration from 0 to 20 seconds. Default 20.
on_pollCallable[[Result[GetJobData]], None] | NoneOptional. Synchronous callback called with each successful poll response.
headersMapping[str, str] | NoneOptional. Additional HTTP headers for this call.

Returns

Result[GetJobData]. data contains the job when the status leaves queued or running, including completed, cancelled, failed or paused. Inspect status. Failed and paused jobs are returned without JobFailedError.

Errors

APIError subclasses follow the HTTP mapping above. APIConnectionError reports transport failures. APITimeoutError reports timed-out attempts. ReverseContactError covers invalid poll_wait or timeout. JobTimeoutError reports an expired deadline. Failed or paused server states are returned for inspection. Malformed typed response data can raise Pydantic ValidationError.

Example

Python SDK

Python SDK

from reversecontact import ReverseContact

with ReverseContact() as client:
    result = client.bulk.wait("00000000-0000-4000-8000-000000000001")
    print(result.data if result is not None else None)

See the Python SDK overview, Lists and bulk and Lists.

FAQ

Does bulk.enrich_persons return a Job handle?

client.bulk.enrich_persons() returns a Result acknowledgement. Read data.job.id, then call client.bulk.wait(job_id).

Does bulk.wait raise when the Bulk job fails?

client.bulk.wait() returns completed, cancelled, failed or paused job states. Inspect data.status. If the wait exceeds its deadline, the SDK raises JobTimeoutError.

How do I download Bulk enrichment results in Python?

Once the job is completed or cancelled, call client.bulk.results(job_id) to download the CSV as text, or client.bulk.results(job_id, format="ndjson") for NDJSON. From SDK 0.3.0, client.bulk.iter_rows(job_id) reads each result as soon as it is ready.

How do I list all Bulk jobs in Python?

Use the free client.bulk.list_all(per_page=100) iterator. With SDK 0.3.0, set per_page explicitly. max_results is optional and limits how many job records the iterator yields.