---
title: "Enrich Person (Bulk)"
method: POST
path: "/v2/people/enrich/bulk"
tags: ["People"]
---

# Enrich Person (Bulk)

`POST /v2/people/enrich/bulk`

## Request

- Base URL: `https://api.village.ai`
- URL: `https://api.village.ai/v2/people/enrich/bulk`
- Auth: HTTP bearer

Get detailed profile information for multiple people in a single request. Use this for batch enrichment of contact lists or CRM records. Provide up to 10 LinkedIn URLs. Returns the same profile data as enrich_person for each. Partial results are returned if some lookups fail - check each result for success/error status. Profiles not already in Village are fetched live from HarvestAPI (public LinkedIn data) and saved to Village. People can have their data removed by emailing privacy@village.ai or support@village.ai.

## Request body

- EnrichBulkRequest: Request for bulk person enrichment (max 10 identifiers)
  - `identifiers` EnrichIdentifier[], required
    - union: Person identifier - provide one of: linkedin_url or url
      - object: Person by LinkedIn URL
        - `linkedin_url` string, required: LinkedIn person profile URL
      - object: Person by generic URL (auto-detected)
        - `url` string, required: Generic URL (auto-detected)
  - `mode` 'cached_first' | 'cached_only' | 'realtime_only': How to source the data. `cached_first` (default): return Village's cached profile if it is recent, otherwise fetch live — credited only when it fetches. `cached_only`: never fetch live, and report a miss when the profile is not already cached (404 on the single endpoints, a per-item not-found on the bulk ones) — never credited. `realtime_only`: always fetch live, and credited whenever that fetch returns data; a fetch that finds nothing is still free.

## Response `200`

Bulk enrichment completed (may include partial failures)

- object
  - `data` union[], required: Response for bulk person enrichment
    - union
      - BasicPersonProfile: Basic person profile with relevant work and full work history
        - `full_name` string, required
        - `linkedin_url` string, required
        - `avatar` string
        - `headline` string, nullable, required: LinkedIn headline
        - `location` Location, nullable, required: Geographic location
          - `city` string, nullable
          - `state` string, nullable
          - `country` string, nullable
        - `work_relevant` WorkRelevant, nullable, required: Current or most relevant work position and company
          - `title` string, required
          - `start_date` string, nullable, required
          - `end_date` string, nullable, required
          - `company` object, nullable, required: Employer information, including employers not yet resolved to Postgres
            - `id` integer, nullable, required: Postgres company ID, or null when the employer has not been resolved
            - `name` string, required: Company name
            - `linkedin_url` string, uri, nullable: LinkedIn company page URL
            - `domain` string, nullable: Company website domain
            - `website` string, nullable: Company website stored in Postgres
            - `industry` string, nullable: Primary industry
            - `employee_count` integer, nullable: Number of employees
            - `image_url` string, uri, nullable: Company logo URL
            - `location` object, nullable: Company HQ location
              - …
            - `headquarters` string, nullable: Raw headquarters string
            - `type` string, nullable: Company type (e.g. Public, Private)
            - `founded_year` integer, nullable: Year the company was founded
            - `funding` union: Funding data, passed through from storage as-is. Two shapes are possible and callers must handle both: an OBJECT summarising the most-recent round (keys `last_round_stage`, `price`, `investors`, `last_round_date`, `number_of_rounds`), or an ARRAY of per-round objects (keys `date`, `type`, `amount`, `currency`, `investors`) written by the refresh pipeline. An empty array means no funding data, which is the large majority of companies. All money, date and count fields are display strings, not numbers or ISO dates — don't parse or range-filter them.
              - …
        - `work_history` WorkExperience[], required
          - `title` string, required
          - `start_date` string, nullable, required
          - `end_date` string, nullable, required
          - `company` object, nullable, required: Employer information, including employers not yet resolved to Postgres
            - `id` integer, nullable, required: Postgres company ID, or null when the employer has not been resolved
            - `name` string, required: Company name
            - `linkedin_url` string, uri, nullable: LinkedIn company page URL
            - `domain` string, nullable: Company website domain
            - `website` string, nullable: Company website stored in Postgres
            - `industry` string, nullable: Primary industry
            - `employee_count` integer, nullable: Number of employees
            - `image_url` string, uri, nullable: Company logo URL
            - `location` object, nullable: Company HQ location
              - …
            - `headquarters` string, nullable: Raw headquarters string
            - `type` string, nullable: Company type (e.g. Public, Private)
            - `founded_year` integer, nullable: Year the company was founded
            - `funding` union: Funding data, passed through from storage as-is. Two shapes are possible and callers must handle both: an OBJECT summarising the most-recent round (keys `last_round_stage`, `price`, `investors`, `last_round_date`, `number_of_rounds`), or an ARRAY of per-round objects (keys `date`, `type`, `amount`, `currency`, `investors`) written by the refresh pipeline. An empty array means no funding data, which is the large majority of companies. All money, date and count fields are display strings, not numbers or ISO dates — don't parse or range-filter them.
              - …
        - `last_enrichment_at` string, nullable, required: ISO 8601 timestamp of the last refresh from a third-party data provider. Null when the profile has never been provider-enriched (network-synced data only). Use it to judge staleness before paying for a realtime_only re-enrich.
      - EnrichError: Enrichment error for a person
        - `id` string, required
        - `found` false, required
        - `error` object, required
          - `code` string, required
          - `message` string, required
  - `metadata` object, required
    - `request_id` string, required

## Other responses

- `400`: Bad request - invalid parameters
- `401`: Unauthorized - missing or invalid authentication

---

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