# ChatGPT Product Feed Generator for Business Hub (CSV / JSONL) (`data_daemon/chatgpt-product-feed-generator`) Actor

Turn a Google Merchant feed, a WooCommerce store or product pages into the product file ChatGPT Business Hub asks for: OpenAI's CSV / JSONL feed with every row checked against the spec (required fields, price, availability, GTIN, variants) and a list of what to fix. $1 per 1,000 products.

- **URL**: https://apify.com/data_daemon/chatgpt-product-feed-generator.md
- **Developed by:** [Daemon Data](https://apify.com/data_daemon) (community)
- **Categories:** E-commerce, AI
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 product rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### ChatGPT product feed generator for Business Hub

**ChatGPT Product Feed Generator** builds the product file that **Businesses in ChatGPT (Business Hub)** asks for: OpenAI's product feed as **CSV or JSONL**, one row per product or variant. It is for store owners, agencies and developers whose shop is **not on Shopify** (Shopify has its own connection): it reads a **Google Merchant feed**, a **WooCommerce store** or the **schema.org product markup** on any shop's pages, maps it to OpenAI's field names (`item_id`, `title`, `description`, `url`, `brand`, `seller_name`, `image_url`, `price`, `availability`, variants, GTIN, sale price and more), checks every row against the spec and tells you what to fix. It costs **$1 per 1,000 product rows**, with no monthly fee. On 11 November 2026 it converted a 100-product WooCommerce store with 354 variations into a 400-row feed in 30 seconds.

This is an independent tool. It is not made or endorsed by OpenAI, and it does not upload anything for you: you get the file, you upload it in Business Hub.

#### What you get from one run

| Output | Where | What it is |
|---|---|---|
| `chatgpt-product-feed.csv` | Key-value store | The feed with OpenAI's column names. Rows that fail a required check are left out. |
| `chatgpt-product-feed.jsonl` | Key-value store | The same feed as JSON Lines |
| `chatgpt-product-feed.jsonl.gz`, `.csv.gz` | Key-value store | Gzipped copies for accounts that deliver feeds to OpenAI over SFTP |
| `validation-report` | Key-value store | Counts of valid, warned and rejected rows, each kind of issue with an example, notes on currency and completeness |
| Dataset | Dataset | Every row, valid or not, with `feed_status` and `feed_issues`, so you can see exactly which products need work |

#### Who uses it

| You are | Use it to |
|---|---|
| A WooCommerce, BigCommerce, Magento, Wix or Squarespace store owner | Get a file for Business Hub without writing code or installing a plugin |
| An agency setting up ChatGPT profiles for clients | Produce and check a feed per client, and hand over the list of rows to fix |
| A developer | Call it from the API or a schedule and keep a daily snapshot of the catalogue in OpenAI's format |
| An AI agent | Convert a store's catalogue on request through the Apify MCP server |

### How to create a ChatGPT product feed

1. Open the **Input** tab and give one source:
   - **Google Merchant feed URL** (best): the link to your Google Shopping feed file (XML, TSV or CSV). Your shop platform or feed plugin shows it in its Google channel settings.
   - **WooCommerce store URL**: just the store's address. No plugin, no login.
   - **Sitemap or product page URLs**: for any other platform; the Actor reads the `Product` markup your pages already print for Google.
2. Set **Seller name** (and **Brand for products without one** if your products carry no brand).
3. Click **Start**. Run it with nothing filled in first if you want to see the output on a sample feed.
4. Open the **Output** tab: download `chatgpt-product-feed.csv` or `.jsonl`, and read the validation report.
5. In ChatGPT **Business Hub**, upload the file as your product feed. Fix the rows the report lists at the source, then run again.

To keep the feed fresh, schedule the Actor (Apify Console → Schedules) and upload the new file, or fetch it from the run's key-value store with the API.

### Input

| Field | What it does |
|---|---|
| `merchantFeedUrls` | Google Merchant / Shopping feed files: XML (RSS or Atom with `g:` fields), TSV or CSV, plain or gzipped |
| `wooCommerceStoreUrls` | WooCommerce stores, read through the public Store API (`/wp-json/wc/store/v1/products`) |
| `sitemapUrls` | Sitemaps or sitemap indexes; product pages in them are opened and their schema.org JSON-LD is read |
| `productPageUrls` | Individual product pages (schema.org `Product` or `ProductGroup`) |
| `productUrlPattern` | Regular expression that picks the product pages out of a sitemap |
| `sellerName` | `seller_name` for every row (required by OpenAI). Read from the source when empty. |
| `brandFallback` | `brand` for products that have none in the source. When empty, the seller name is used and the row gets a note. |
| `sellerUrl`, `returnPolicyUrl`, `returnDeadlineDays` | Optional seller columns added to every row |
| `currency` | Used only when a price has no currency code |
| `urlSuffix` | Query string added to every product link, for example `utm_source=chatgpt.com&utm_medium=feed` |
| `expandVariants` | One row per WooCommerce variation (default) or one row per product |
| `includeOutOfStock` | Keep out-of-stock products with `availability: out_of_stock` (default) or leave them out |
| `maxItems` | Cap for test runs. 0 = whole catalogue |

```json
{
    "wooCommerceStoreUrls": ["https://shop.example.com"],
    "sellerName": "Example Outdoor",
    "brandFallback": "Example Outdoor",
    "returnPolicyUrl": "https://shop.example.com/returns",
    "returnDeadlineDays": 30
}
```

### Output: OpenAI product feed fields

One line of `chatgpt-product-feed.jsonl` from the sample feed:

```json
{"item_id":"JR-MUG-350-GRN","title":"Enamel camp mug, 350 ml, forest green","description":"Steel camp mug with a speckled enamel coating. Holds 350 ml. Safe on a gas stove and in the dishwasher.","url":"https://www.example.com/products/enamel-camp-mug?color=green","brand":"Juniper Ridge","seller_name":"Juniper Ridge Outfitters","image_url":"https://www.example.com/images/enamel-camp-mug-green.jpg","price":"18.00 USD","sale_price":"14.40 USD","availability":"in_stock","group_id":"JR-MUG-350","listing_has_variations":true,"variant_dict":{"color":"Forest green"},"mpn":"JR-350-G","condition":"new","product_category":"Camp Kitchen > Drinkware > Mugs","material":"Enamelled steel","color":"Forest green","weight":"0.21","item_weight_unit":"kg","shipping_price":"5.00 USD"}
```

| Field | Filled from |
|---|---|
| `item_id`, `title`, `description`, `url`, `image_url` | The product's id, name, description (HTML removed), page and main image |
| `brand`, `seller_name` | The source, or your `brandFallback` / `sellerName` |
| `price`, `sale_price` | Regular price and the lower sale price, written as `79.99 USD` |
| `availability` | `in_stock`, `out_of_stock`, `pre_order`, `backorder` |
| `group_id`, `listing_has_variations`, `variant_dict` | Variant grouping: Google `item_group_id` with colour / size, WooCommerce variations, schema.org `ProductGroup` |
| `gtin`, `mpn`, `condition`, `product_category` | The source, when present and valid |
| `color`, `size`, `material`, `gender`, `age_group` | Google feed columns, variation options or page markup |
| `weight`, `item_weight_unit`, `length`, `width`, `height`, `dimensions_unit` | Product weight and dimensions with their unit |
| `additional_image_urls`, `shipping_price`, `review_count`, `star_rating` | More images, the US shipping entry of a Google feed, product ratings |
| `seller_url`, `return_policy`, `accepts_returns`, `return_deadline_in_days` | Your input |

Dataset rows carry the same fields plus `feed_status` (`valid`, `valid_with_warnings`, `rejected`), `feed_issues`, `source_type`, `source_url` and `generated_at`. You can download the dataset as JSON, CSV, Excel or HTML.

#### What the validator checks

- **Required fields**: the nine fields OpenAI requires on every row. A row missing one is rejected and left out of the files.
- **Price**: positive, `amount CURRENCY` with an ISO 4217 code and no more decimals than the currency allows. It reads `$1,299.00`, `79,99 EUR` and `USD 79.99`.
- **Availability**: mapped from Google, WooCommerce and schema.org values; anything else rejects the row and the message names the value.
- **Sale price**: kept only when it is lower than the price and in the same currency.
- **GTIN**: 8, 12, 13 or 14 digits with a valid check digit; an invalid one is left out with a warning, so one bad barcode does not cost you the row.
- **Unique ids** and **variant groups**: repeated `item_id`, groups whose variants use different option names, two variants with the same options.
- **Text**: HTML removed from descriptions, descriptions over 5,000 characters cut at a word boundary, titles over 150 characters flagged.
- **Images and links**: absolute `http(s)` URLs; AVIF, WebP and SVG main images are flagged because OpenAI asks for a direct image such as JPEG or PNG.
- **Placeholders**: `n/a`, `null`, `unknown` and similar in a required field reject the row.

The checks follow OpenAI's [product feed spec](https://developers.openai.com/commerce/specs/file-upload/products) as published on 11 November 2026. OpenAI decides what it accepts and shows; a clean report here means the file matches the published format, not that every product will appear in ChatGPT.

### How much does a ChatGPT product feed cost?

**$0.001 per product row** ($1 per 1,000), charged for each row written to the dataset, valid or not. Rows left out by `maxItems` or by `includeOutOfStock: false` are not charged. No start fee, no subscription; Apify platform usage is included.

| Catalogue | One run | Daily refresh for a month |
|---|---|---|
| Sample feed (24 rows) | $0.02 | - |
| 500 products | $0.50 | $15 |
| 2,000 products | $2.00 | $60 |
| 10,000 products | $10.00 | $300 (weekly: about $43) |

Apify's free plan includes $5 of monthly credit, enough for a 5,000-row feed. Set a maximum charge per run in the run options if you want a hard limit.

### Tips

- A **Google Merchant feed** gives the best result: it already has brand, GTIN, variant groups and sale prices. A WooCommerce store without a brand taxonomy or barcodes produces a valid but thinner feed.
- OpenAI treats each upload as the **whole catalogue**. Do not upload a file from a capped or failed run as your live feed; the report's `complete` flag tells you.
- OpenAI's standard upload targets the **US**. If your prices are not in USD the report says so; other markets need OpenAI to confirm them for your account.
- Products with **no description** get the title as description and a warning. Write real descriptions for those first: that text is what ChatGPT matches to a shopper's question.
- Feed files are read into memory. If a very large feed stops with an out-of-memory error, raise the memory in the run options.

### FAQ

#### What is Businesses in ChatGPT (Business Hub)?

OpenAI opened it in beta on 10 November 2026. A business claims its profile at `chatgpt.com/business-hub`, verifies it, and can then edit its details, see how often ChatGPT mentions and links to it, and add a product catalogue. Shopify stores connect an app; other stores upload a product file in OpenAI's format, which is what this Actor produces.

#### Does this upload the feed to ChatGPT for me?

No. The Actor never signs in to your ChatGPT account, and claiming a profile needs your own verification. You download the file from the run and upload it yourself.

#### Does a product feed make ChatGPT recommend my products?

OpenAI says claiming a profile does not guarantee placement. The feed gives ChatGPT accurate names, prices, stock and links for your products; whether it shows them is OpenAI's decision.

#### Can it read a Shopify store?

Shopify stores can use OpenAI's Shopify connection, so this Actor does not read Shopify's storefront JSON. If you want a file anyway, pass the Google Shopping feed URL from your Shopify feed app, or your sitemap.

#### My WooCommerce store returns an error. Why?

Some stores switch off the public Store API or sit behind a bot check. Pass the store's Google Merchant feed URL instead (any WooCommerce feed plugin gives you one), or its sitemap, or turn on Apify Proxy in the input.

#### Is there an API for this?

Yes. Start a run with the [Apify API](https://docs.apify.com/api/v2), wait for it to finish and download `chatgpt-product-feed.jsonl` from the run's key-value store. Python and Node.js clients, n8n, Make and Zapier work the same way, and AI agents can call it through `https://mcp.apify.com/?tools=data_daemon/chatgpt-product-feed-generator`.

#### Is it legal to read these pages?

The Actor reads what a shop publishes for search engines and shopping channels: its feed file, its public product API and its product pages. Run it on your own store or one you are authorised to work on.

### Support

Found a feed or a store it reads wrongly? Open an issue on the **Issues** tab with the feed or page URL and the row that looks wrong. More data tools from the same publisher: [Daemon Data on Apify](https://apify.com/data_daemon).

# Actor input Schema

## `merchantFeedUrls` (type: `array`):

Links to product feed files in Google Merchant Center format: XML (RSS or Atom with `g:` fields), TSV or CSV, plain or gzipped. Most shop platforms and feed plugins can give you this link (WooCommerce feed plugins, BigCommerce, Magento, Wix, Squarespace, Shopify feed apps). This is the most complete source because the file already has brand, GTIN, variants and sale prices. Leave every source empty to convert a sample feed and see the output.

## `wooCommerceStoreUrls` (type: `array`):

Home page of a WooCommerce store, for example `https://shop.example.com`. The Actor reads the store's public product API (`/wp-json/wc/store/v1/products`): every published product with price, stock, images, categories, ratings and variations. No plugin and no login. If the store has switched that API off, use its feed URL or sitemap instead.

## `sitemapUrls` (type: `array`):

Sitemap or sitemap index of a shop, for example `https://shop.example.com/sitemap.xml`. Product pages found in it are opened and their schema.org `Product` markup (JSON-LD) is read. Works on any platform that prints that markup, which most do for Google.

## `productPageUrls` (type: `array`):

Individual product pages to read (schema.org `Product` / `ProductGroup` JSON-LD). Use it for a small catalogue or to test a few pages.

## `productUrlPattern` (type: `string`):

Regular expression that tells product pages apart from other pages in a sitemap. Default `/(products?|p|item|shop|dp)/`.

## `sellerName` (type: `string`):

Your store's name as customers should see it. OpenAI requires `seller_name` on every row. When empty, the name is read from the feed title, the WooCommerce site name or the page's site name.

## `brandFallback` (type: `string`):

OpenAI requires `brand` on every row. Products that have a brand in the source keep it. For the rest this value is used; when it is empty the seller name is used and the row gets a warning.

## `sellerUrl` (type: `string`):

Optional `seller_url`: your storefront's home page.

## `returnPolicyUrl` (type: `string`):

Optional `return_policy`: public page with your return terms.

## `returnDeadlineDays` (type: `integer`):

Optional. When set, every row gets `accepts_returns: true` and `return_deadline_in_days`. Leave 0 to leave returns unspecified.

## `currency` (type: `string`):

Three-letter ISO code, for example `USD`. Only used when a price has no currency code (a bare `19.99` or `$19.99`).

## `urlSuffix` (type: `string`):

Optional query string added to every product `url`, for example `utm_source=chatgpt.com&utm_medium=feed`. Parameters already on the link are kept.

## `expandVariants` (type: `boolean`):

OpenAI wants one row per purchasable variant, linked by `group_id` with the options in `variant_dict`. Turn off to write one row per product.

## `includeOutOfStock` (type: `boolean`):

Out-of-stock products stay in the feed with `availability: out_of_stock` (recommended: OpenAI treats each upload as the full catalogue). Turn off to leave them out; they are then not charged.

## `maxItems` (type: `integer`):

Stop after this many product rows (for a test run). 0 = the whole catalogue. A capped file is not a full snapshot, so do not upload it as your live feed.

## `proxyConfiguration` (type: `object`):

Not needed for feed files. Turn on Apify Proxy if a store blocks requests from data centers.

## Actor input object example

```json
{
  "merchantFeedUrls": [
    "https://datadmon.github.io/data/samples/google-merchant-sample-feed.xml"
  ],
  "wooCommerceStoreUrls": [],
  "sitemapUrls": [],
  "productPageUrls": [],
  "productUrlPattern": "/(products?|p|item|shop|dp)/",
  "returnDeadlineDays": 0,
  "expandVariants": true,
  "includeOutOfStock": true,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `feedCsv` (type: `string`):

The file to upload in ChatGPT Business Hub: one row per product or variant, OpenAI's column names, rejected rows left out.

## `feedJsonl` (type: `string`):

The same feed as JSON Lines.

## `feedJsonlGz` (type: `string`):

Gzipped JSONL for accounts that deliver the feed to OpenAI over SFTP.

## `report` (type: `string`):

Counts of valid, warned and rejected rows, each kind of issue with an example, the sources read and notes about currency and completeness.

## `dataset` (type: `string`):

Every row, valid or not, with feed_status and feed_issues, as JSON, CSV or Excel.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "merchantFeedUrls": [
        "https://datadmon.github.io/data/samples/google-merchant-sample-feed.xml"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("data_daemon/chatgpt-product-feed-generator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "merchantFeedUrls": ["https://datadmon.github.io/data/samples/google-merchant-sample-feed.xml"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("data_daemon/chatgpt-product-feed-generator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "merchantFeedUrls": [
    "https://datadmon.github.io/data/samples/google-merchant-sample-feed.xml"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call data_daemon/chatgpt-product-feed-generator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data_daemon/chatgpt-product-feed-generator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/6cuh8ouL5WfAtpXi1/builds/cS8unR2QFHPWKkY3P/openapi.json
