---
title: "Run Ai Enrichment"
method: POST
path: "/enrichment/ai/enrich"
tags: ["Enrichment"]
---

# Run Ai Enrichment

`POST /enrichment/ai/enrich`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/enrichment/ai/enrich`
- Auth: HTTP bearer

Run a saved AI enrichment config (web-research / content-gen) on records.

Delegates to ``AIEnrichmentHandler`` (the same handler the UI uses).
Unlike the waterfall endpoint - which queues an async job - this call
runs synchronously: ``handler.run()`` blocks until every record has
been processed (typical: 30-120 s for a handful of contacts on the
web-research usecase). The response therefore returns ``status="completed"``
with the final counters inline, not a queued job_id to poll.

The job_id is still returned for audit / cross-reference - polling it
via ``GET /jobs/{job_id}`` works thanks to the DB fallback on that
endpoint, but most callers will get everything they need from this
response directly.

Credits are charged up-front at the web-research baseline
(``CREDITS_AI_ENRICH_ESTIMATE``). The handler does not charge
additionally, so this is the only credit deduction per call.

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- RunAIEnrichRequest: Run a saved AI enrichment config on specific records. The prompt, model, outputs, and column mapping live on the config referenced by `group_id`. We accept `record_ids` so callers can target exactly the contacts they want enriched.
  - `group_id` string, required: group_id of a saved AI enrichment config (starts with 'ai_enrich')
  - `list_id` integer, required: List ID the records belong to
  - `record_ids` integer[], required: Contact IDs to enrich
  - `max_records` integer, nullable: Optional cap on records processed (defaults to len(record_ids)).
  - `skip_existing_values` boolean, nullable: Override the config's skip_existing_values setting.

## Response `200`

Successful Response

- ApiResponseEnrichJobResponse
  - `data` EnrichJobResponse, required
    - `job_id` string, required
    - `status` string, required
    - `total` integer, nullable
    - `completed` integer, nullable
    - `results` object[], nullable
    - `successful_enrichments` integer, nullable
    - `failed_enrichments` integer, nullable
    - `total_credits_used` integer, nullable
    - `warnings` string[], nullable
    - `provider_errors` object[], nullable
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
