Brand Enrichment API
AI-powered enrichment that generates rich brand profiles — descriptions, taglines, founding year, SEO fields, custom attributes, and more — using live web research or your linked product catalogue.
Overview
The Brand Enrichment API enriches brand records with structured attributes you define under Settings → Enrichment → Brand Attributes. Each job uses your active attributes (or a specific Attribute Set) and either researches brands via live web search or grounds enrichment in your linked product catalogue.
Jobs run asynchronously, processing up to 2,000 brands per job with 5 brands processed concurrently.
Endpoints Overview
| Method | Endpoint | Description |
|---|---|---|
| POST | /brand-enrichment-jobs/create |
Create a batch enrichment job |
| POST | /brand-enrichment-jobs/get |
List enrichment jobs |
| POST | /brand-enrichment-jobs/{job_id}/items/get |
Get a job's enriched results |
| POST | /brand-enrichment-jobs/{job_id}/stop |
Stop a running job |
| GET | /brand-enrichment-jobs/{job_id}/download |
Download job results as CSV |
All endpoints are prefixed with /api/{organization}.
Create a Batch Enrichment Job
Start a job to enrich multiple brands in parallel. The job runs asynchronously — brands are processed concurrently (up to 5 at a time) in the background.
Endpoint: POST /api/{organization}/brand-enrichment-jobs/create
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
rows |
object[] | Yes | — | Array of brand records. Each row must include a name, brand, brand_name, or brandName key. 1–2,000 rows. |
inputColumns |
string[] | Yes | — | List of column names present in rows |
fileName |
string | No | — | Display name shown in the jobs list (max 255 characters, recommended to end with .csv) |
modelType |
string | No | budget-v1 |
AI model to use: budget-v1 or pro-v1 |
attributeSetId |
string | No | — | ID of a specific Attribute Set; omit to use all active attributes |
useWebsearch |
boolean | No | true |
Allow the AI to search the web for brands that have no linked products. Set to false if you only want product-grounded enrichment |
includeExistingAttributes |
boolean | No | true |
Pass the latest completed attribute values for each brand to the model. Set false to regenerate without keeping old specs. |
Model Types
| Value | Description |
|---|---|
budget-v1 |
Cost-effective, fast. Recommended for bulk runs. |
pro-v1 |
Higher quality. Best for flagship brands. |
Limits
| Limit | Value |
|---|---|
| Max rows per job | 2,000 |
| Max CSV size | 8 MB |
| Concurrency | 5 brands at a time |
Note: Only one enrichment job can run at a time per organisation. If a job is already
pendingorprocessing, the request returns an error asking you to wait.
Brand Name Column Detection
The brand name is detected from the row using the first non-blank match from these keys, in order:
name → brand → brand_name → brandName
Enrichment Modes
Each brand is processed in one of two modes chosen automatically at runtime:
| Mode | Triggered when | Behaviour |
|---|---|---|
products |
Brand has ≥ 1 linked product in your catalogue | AI uses up to 10 sampled products as grounding; no web search |
websearch |
Brand has no linked products and useWebsearch is true |
AI performs live web searches and fetches brand pages |
If a brand has no linked products and useWebsearch is false, that item fails with the error: "No products are linked to this brand. Link products via Enrich Brand on a product, or enable "Use Websearch" before enriching."
Example Request
curl -X POST https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/create \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rows": [
{ "name": "Nike", "country": "US" },
{ "name": "Adidas", "country": "DE" }
],
"inputColumns": ["name", "country"],
"fileName": "brands.csv",
"modelType": "budget-v1",
"useWebsearch": true
}'
Response
{
"success": true,
"data": {
"success": true,
"message": "Brand enrichment job created successfully",
"jobId": "bej_abc123"
}
}
List Enrichment Jobs
Retrieve a paginated list of brand enrichment jobs for the organisation.
Endpoint: POST /api/{organization}/brand-enrichment-jobs/get
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
page |
number | No | 1 |
Page number (min 1) |
limit |
number | No | 20 |
Items per page (max 100) |
Example Request
curl -X POST https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/get \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"page": 1, "limit": 20}'
Response
{
"success": true,
"data": {
"success": true,
"jobs": [
{
"id": "bej_abc123",
"status": "completed",
"modelType": "budget-v1",
"fileName": "brands.csv",
"useWebsearch": true,
"attributeSetId": null,
"inputColumns": ["name", "country"],
"totalBrands": 200,
"processedCount": 200,
"successCount": 198,
"failedCount": 2,
"errorSummary": null,
"startedAt": "2026-05-18T08:00:00Z",
"completedAt": "2026-05-18T08:06:42Z",
"createdAt": "2026-05-18T07:59:55Z",
"createdBy": {
"name": "Aditya",
"email": "aditya@example.com"
}
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
}
}
}
Job Status Values
| Status | Description |
|---|---|
pending |
Job is queued and will start shortly |
processing |
Brands are being enriched right now |
completed |
All brands finished successfully |
completed_with_errors |
Job finished but some brands failed |
failed |
Job failed before processing |
stopped |
Job was manually stopped |
Get Job Items
Retrieve detailed enriched results for a specific job, paginated by row.
Endpoint: POST /api/{organization}/brand-enrichment-jobs/{job_id}/items/get
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id |
string | Yes | Job ID |
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
page |
number | No | 1 |
Page number (min 1) |
limit |
number | No | 50 |
Items per page (max 100) |
Example Request
curl -X POST https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/bej_abc123/items/get \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"page": 1, "limit": 50}'
Response
{
"success": true,
"data": {
"success": true,
"job": {
"id": "bej_abc123",
"modelType": "budget-v1",
"fileName": "brands.csv",
"inputColumns": ["name", "country"],
"totalBrands": 200,
"processedCount": 200,
"successCount": 198,
"failedCount": 2,
"status": "completed",
"errorSummary": null
},
"items": [
{
"id": "item_001",
"jobId": "bej_abc123",
"rowIndex": 0,
"brandName": "Nike",
"inputData": {
"name": "Nike",
"country": "US"
},
"enrichedAttributes": {
"brand_description": {
"attributeId": "attr_001",
"attributeName": "Brand Description",
"attributeType": "text",
"value": "Nike is a global leader in athletic footwear, apparel, and equipment..."
},
"tagline": {
"attributeId": "attr_002",
"attributeName": "Tagline",
"attributeType": "text",
"value": "Just Do It"
},
"founded_year": {
"attributeId": "attr_003",
"attributeName": "Founded Year",
"attributeType": "number",
"value": 1964
},
"page_title": {
"attributeId": "attr_004",
"attributeName": "Page Title",
"attributeType": "text",
"value": "Nike — Athletic Footwear, Apparel & Equipment"
}
},
"referenceData": {
"mode": "websearch",
"urls": [
"https://www.nike.com/",
"https://en.wikipedia.org/wiki/Nike,_Inc."
]
},
"status": "completed",
"cost": 0.0042,
"startedAt": "2026-05-18T08:00:05Z",
"completedAt": "2026-05-18T08:00:18Z"
},
{
"id": "item_002",
"jobId": "bej_abc123",
"rowIndex": 1,
"brandName": "UnknownBrand",
"inputData": {
"name": "UnknownBrand",
"country": "US"
},
"referenceData": null,
"status": "failed",
"errorMessage": "No products are linked to this brand. Link products via Enrich Brand on a product, or enable \"Use Websearch\" before enriching."
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 200,
"totalPages": 4
}
}
}
enrichedAttributes Object
Each key in enrichedAttributes is the attribute slug (e.g. brand_description, page_title). The value object has the following fields:
| Field | Type | Description |
|---|---|---|
attributeId |
string | Internal attribute ID |
attributeName |
string | Human-readable attribute name |
attributeType |
string | One of text, html, number, boolean, select, multiselect |
value |
string | number | boolean | string[] | null | The enriched value |
referenceData Object
| Field | Type | Description |
|---|---|---|
mode |
string | "products" or "websearch" |
productIds |
string[] | (products mode only) IDs of the sampled products used as context |
urls |
string[] | (websearch mode only) URLs fetched during web research |
Item Status Values
| Status | Description |
|---|---|
pending |
Waiting to be processed |
processing |
Currently being enriched |
completed |
Successfully enriched |
failed |
Failed (see errorMessage) |
Stop a Job
Stop a pending or processing job. Brands already enriched keep their results. All remaining pending and processing items are immediately marked as failed with the message "Job was stopped by user".
Endpoint: POST /api/{organization}/brand-enrichment-jobs/{job_id}/stop
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id |
string | Yes | ID of the job to stop |
Example Request
curl -X POST https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/bej_abc123/stop \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"
Response
{
"success": true,
"message": "Brand enrichment job stopped successfully",
"data": {
"jobId": "bej_abc123",
"status": "stopped"
}
}
Error Responses
If the job does not exist:
{
"success": false,
"message": "Job not found"
}
If the job is already completed, failed, or stopped:
{
"success": false,
"message": "Job cannot be stopped because it is not in progress"
}
Notes
- Only jobs with status
pendingorprocessingcan be stopped - All
pendingandprocessingitems are immediately markedfailedwith error"Job was stopped by user"
Download Job Results as CSV
Download all enriched rows as a CSV file. The file preserves your original input columns and appends enrichment status and one column per configured attribute.
Endpoint: GET /api/{organization}/brand-enrichment-jobs/{job_id}/download
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id |
string | Yes | Job ID |
Example Request
curl -X GET https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/bej_abc123/download \
-H "Authorization: Bearer YOUR_API_KEY" \
-o enriched-brands.csv
Response
A text/csv file with Content-Disposition: attachment. The filename format is:
{original_filename_without_extension}_enriched_{YYYY-MM-DD}.csv
Falls back to brands_{first8charsOfJobId}_enriched_{YYYY-MM-DD}.csv when no file name is set.
The header row is structured as:
<your input columns>, Enrichment Status, Enrichment Error, <one column per enriched attribute>
Attribute column notes:
htmltype attributes are written as raw HTML; any other attribute whose value contains HTML tags is automatically stripped to plain textmultiselectvalues are joined with;booleanvalues are written asYes/No- All other types are written as plain strings
nullvalues are written as empty strings
Workflow Example
1. Prepare Brand Rows
const brands = [
{ name: 'Nike', country: 'US' },
{ name: 'Adidas', country: 'DE' },
{ name: 'Sony', country: 'JP' }
]
2. Create the Job
const createRes = await fetch(
'https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/create',
{
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
rows: brands,
inputColumns: ['name', 'country'],
modelType: 'budget-v1',
useWebsearch: true
})
}
)
const { data } = await createRes.json()
const jobId = data.jobId
3. Poll for Completion
async function waitForJob(jobId) {
while (true) {
const res = await fetch(
'https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/get',
{
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ page: 1, limit: 100 })
}
)
const { data } = await res.json()
const job = data.jobs.find(j => j.id === jobId)
if (
job &&
['completed', 'completed_with_errors', 'failed', 'stopped'].includes(
job.status
)
) {
return job
}
await new Promise(r => setTimeout(r, 5000))
}
}
const finalJob = await waitForJob(jobId)
4. Fetch Enriched Items
const itemsRes = await fetch(
`https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/${jobId}/items/get`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ page: 1, limit: 50 })
}
)
const { data } = await itemsRes.json()
console.log(data.items)
5. Or Download the Enriched CSV
const csvRes = await fetch(
`https://catalog-ai.tdcapps.com/api/your-org/brand-enrichment-jobs/${jobId}/download`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
)
const csv = await csvRes.text()
fs.writeFileSync('enriched-brands.csv', csv)
Best Practices
- Include a recognised name column — the row must have one of
name,brand,brand_name, orbrandName; this is used to identify the brand. - Link products before enriching — brands with linked catalogue products produce richer, more accurate output than web search mode; the AI samples up to 10 products per brand.
- Enable
useWebsearchfor unlinked brands — without linked products and with websearch off, those items will fail immediately. - Use
attributeSetId— restrict the job to a named Attribute Set when you only need to refresh specific fields; faster and cheaper. - Stay under 2,000 rows per job — split larger brand lists into multiple jobs.
- Poll every 5–10 seconds — enrichment is asynchronous; avoid hammering the endpoint.
- Check
failedCountand itemerrorMessage— common failures are no linked products with websearch off, or credit exhaustion mid-job. - Use
pro-v1for flagship brands — for brands where content quality matters most, the Pro model produces noticeably better output.
Error Responses
400 Bad Request
Invalid request body or missing required fields:
{
"success": false,
"message": "Invalid request body: ..."
}
401 Unauthorized
Missing API key:
{
"success": false,
"message": "Missing API Key"
}
403 Forbidden
Invalid or revoked API key, or the key does not have access to the specified organisation:
{
"success": false,
"message": "Invalid API Key"
}
409 Conflict — Job Already in Progress
A job is already pending or processing for this organisation:
{
"success": false,
"message": "A brand enrichment job is already in progress. Please wait for it to complete before starting a new one."
}
402 Payment Required — Credit Balance Exhausted
Triggered at job creation if the credit balance is zero:
{
"success": false,
"message": "Credit limit reached. Please top up your credits to start a new enrichment job."
}
If credits exhaust mid-job, the job continues processing but remaining items fail with the error "Credit balance limit has been reached." The job's errorSummary is set to "Credit balance limit has been reached." Top up and create a new job to continue.
500 Internal Server Error
{
"success": false,
"message": "Internal Server Error"
}