---
title: "Bulk-enrich contacts (webhook delivery)"
method: POST
path: "/api/v1/enrichment/contacts/bulk"
tags: ["Enrichment"]
---

# Bulk-enrich contacts (webhook delivery)

`POST /api/v1/enrichment/contacts/bulk`

## Request

- Base URL: `https://api.rodz.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.rodz.io/api/v1/enrichment/contacts/bulk`
- Auth: HTTP bearer

Enrich up to **50,000** LinkedIn profiles in a single call.

Bulk enrichment runs **asynchronously**: this call returns immediately with a `batchId`, and the results arrive when the batch completes.

**Data freshness — you choose with `freshness`:**
- `live` (default): a fresh scrape of every profile — most current data, **0.5 credits per match**. Slower; very large batches can take a while.
- `cached`: served from our directory cache — **0.25 credits per match (half price)** and much faster (typically ~1 min). Cached profiles are **never more than 8 days old**.

**Delivery — you choose with `deliveryMode`:**
- `webhook` (default): the **full enriched batch** is POSTed to your `webhookUrl` when it finishes. Simplest for small/medium batches.
- `poll`: results are **held for you to fetch** at `GET /api/v1/enrichment/contacts/bulk/{batchId}` (paginated). Nothing large is pushed; if you also pass a `webhookUrl`, you receive a small **ready-notification** (not the full payload) when the batch is done.

> **Recommended: use `deliveryMode: poll` for any request over 5,000 profiles.** A completed 50,000-profile batch is a 50–150 MB payload; polling lets you pull it in pages instead of receiving one very large POST (which many servers reject or time out on).

**Cost:** per successfully enriched profile — **0.5 credits** (`freshness: live`) or **0.25 credits** (`freshness: cached`). No charge for profiles that can't be matched, and the same whether you poll or use a webhook.

**Request:** `profiles` (each a LinkedIn `url` + optional `externalId` you'll get back), `deliveryMode`, `freshness`, and a `webhookUrl` (required for `webhook`, optional for `poll`).

**Immediate response (202):**
```json
{ "success": true, "data": { "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "accepted": 2, "deliveryMode": "webhook", "freshness": "live", "pollUrl": null } }
```
For `deliveryMode: poll`, `pollUrl` is `/api/v1/enrichment/contacts/bulk/{batchId}`.

**Webhook payload** (POSTed to your `webhookUrl` when the batch finishes) — `person` is `null` when a profile can't be matched, otherwise a full profile:
```json
{
  "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "total": 2,
  "found": 1,
  "results": [
    {
      "externalId": "crm-123",
      "url": "https://www.linkedin.com/in/marie-dupont",
      "person": {
        "firstName": "Marie", "lastName": "Dupont", "name": "Marie Dupont",
        "linkedInUrl": "https://www.linkedin.com/in/marie-dupont",
        "linkedInId": "251749025", "linkedinSalesLink": "ACoAAB1a2b3c4D",
        "headline": "Head of Sales at Lemlist",
        "seniority": "director", "jobFunction": "sales", "subRole": "sales_management",
        "industry": "Software Development",
        "activeCompany": "Lemlist", "currentCompanyId": 12345678,
        "location": "Paris, Île-de-France, France", "country": "FR", "region": "eu", "geoId": 105015875,
        "photoUrl": "https://media.licdn.com/dms/image/abc123",
        "followers": "4200", "followerCount": 4200, "connectionsCount": 500,
        "linkedinSummary": "Helping outbound teams book more meetings.",
        "skillsList": "Sales, SaaS, Team Leadership",
        "skillsDetail": [ { "name": "Sales", "endorsements": 42 }, { "name": "SaaS", "endorsements": 17 } ],
        "languages": "French, English",
        "languagesDetail": [ { "name": "French", "proficiency": "native" }, { "name": "English", "proficiency": "professional" } ],
        "currentCompanyDetail": [
          {
            "companyName": "Lemlist", "companyId": 12345678, "jobTitle": "Head of Sales",
            "startTime": "2023-01-01", "endTime": "Present",
            "companyUrl": "https://www.linkedin.com/company/lemlist",
            "companyWebsite": "https://lemlist.com",
            "companyIndustry": "Software Development", "companyStaffRange": "51-200",
            "companyHqCity": "Paris", "companyCountryCode": "FR"
          }
        ],
        "pastCompaniesDetails": [
          { "companyName": "Acme", "jobTitle": "Account Executive", "startTime": "2020-01-01", "endTime": "2022-12-01" }
        ],
        "formerCompaniesList": "Acme",
        "education": {
          "universitiesNamesList": "HEC Paris",
          "educationDetails": [ { "universityName": "HEC Paris", "degree": "MBA", "fieldsOfStudy": ["Marketing"], "startedOn": { "year": "2016" }, "endedOn": { "year": "2018" } } ]
        }
      }
    },
    { "externalId": "crm-456", "url": "https://www.linkedin.com/in/jean-martin", "person": null }
  ]
}
```

**Each `person` returns:** identity (`firstName`, `lastName`, `name`, `linkedInUrl`, `linkedInId`, `linkedinSalesLink`, `photoUrl`); role (`headline`, `seniority`, `jobFunction`, `subRole`, `industry`); location (`location`, `country` + `countryName`, `region` + `regionName`, `geoId`); reach (`followers`, `followerCount`, `connectionsCount`); `linkedinSummary`; skills (`skillsList` + `skillsDetail` with endorsement counts); languages (`languages` + `languagesDetail` with proficiency); current & past positions with tenure (`yearsInRole`, `yearsInCompany`) and company firmographics (`currentCompanyDetail` / `pastCompaniesDetails` — company industry, staff range, HQ, website, …); and `education` (with degrees). Any field is `null` when the profile doesn't have it. Best coverage comes from LinkedIn profile URLs.

## Request body

- object: Bulk contact-enrichment request (results delivered by webhook).
  - `profiles` object[], required: Profiles to enrich (1–50,000 per request)
    - `url` string, required: LinkedIn profile URL to enrich
    - `externalId` string, nullable: Your own reference for this profile — echoed back in the webhook payload
  - `deliveryMode` 'webhook' | 'poll': How to receive results. `webhook` (default): the full enriched batch is POSTed to your `webhookUrl` when it completes. `poll`: results are held for you to fetch via `GET /api/v1/enrichment/contacts/bulk/{batchId}` (paginated) — nothing large is pushed; if you also pass a `webhookUrl` you get a small ready-notification when the batch finishes. **Use `poll` for requests over 5,000 profiles** so a 50,000-profile result isn't delivered in a single push.
  - `freshness` 'live' | 'cached': Data freshness. `live` (default): a fresh scrape of each profile — most current data, full price (0.5 credits/match), and slower (large batches can take a while). `cached`: served from our directory cache — **half price (0.25 credits/match)** and much faster (typically ~1 min), at the cost of data that may be slightly less recent. Charged per successful match either way.
  - `webhookUrl` string, nullable: HTTPS URL to receive results. Required for `deliveryMode: webhook` (receives the full batch). Optional for `deliveryMode: poll` (receives only a small ready-notification; omit it to poll silently).

## Response `202`

Successful Response

- object: Immediate 202 acknowledgement — results arrive later via the webhook.
  - `success` boolean
  - `data` object, required
    - `batchId` string, required: Identifier for this batch, echoed in the webhook / poll response
    - `accepted` integer, required: Number of profiles accepted for enrichment
    - `deliveryMode` string: How results will be delivered: `webhook` or `poll`
    - `freshness` string: Data freshness for this batch: `live` or `cached`
    - `pollUrl` string, nullable: For `deliveryMode: poll` — the path to GET the finished batch (paginated)

## Other responses

- `422`: Validation Error
- `429`: Rate limit exceeded. Retry after the number of seconds in the `Retry-After` response header.

---

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