For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Primary navigation

Product Feeds

Set up, update, and advertise a product catalog.

Use product feeds to create ads from your catalog. Upload your product data, select the products an ad group can use, and create an ad template that fills in product details when an ad is shown.

Examples use an ad account-scoped Advertiser API key, ${OPENAI_ADS_API_KEY}, and sample IDs such as fd_123, cmpn_123, adgrp_123, and ad_123. Replace these with the IDs returned by your requests. Keep the API key on your server. See Authentication for request conventions.

The examples use a US catalog with USD prices. Use the countries, product information, and currency appropriate to your catalog and ad account.

Feed Setup & Ingestion

Create a feed in your ad account, configure SFTP access, and upload your catalog. SFTP is the file-transfer connection used to send product files to the feed.

Before you start

You will need:

  • An ad account and an Advertiser API key with permission to manage its feeds.
  • Product feed API access for the account.
  • A catalog that follows the OpenAI product file schema.
  • Publicly accessible product pages and product images.
  • An SFTP client or an application that can upload files over SFTP.

If feed access is unavailable, contact your OpenAI account team before continuing.

1. Create a feed

Create a feed with a name and the countries your catalog supports.

curl -X POST "https://api.ads.openai.com/v1/feeds" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme spring catalog",
    "countries": ["US"]
  }'

Save the returned feed_id and use it in place of fd_123 below.

2. Configure SFTP access

Use password authentication or an SSH public key.

Password authentication

curl -X POST "https://api.ads.openai.com/v1/feeds/fd_123/sftp_access" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "authentication_method": "password"
  }'

Use the returned connection URI and password to connect your SFTP client. Store the password securely. Generating a new password replaces the previous one.

SSH-key authentication

Send your public key as ssh_public_key. Replace the example value with the complete contents of your public-key file.

curl -X POST "https://api.ads.openai.com/v1/feeds/fd_123/sftp_access" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "authentication_method": "ssh_key",
    "ssh_public_key": "YOUR_SSH_PUBLIC_KEY"
  }'

Configure your SFTP client with the corresponding private key.

3. Prepare the product file

Create a UTF-8 CSV with one row per product item or variant. Keep product identifiers stable between uploads.

The following example shows a two-product catalog. Replace the example URLs with your real product and image URLs before uploading it.

item_id,title,description,url,brand,image_url,price,availability,seller_name,seller_url,return_policy,target_countries,store_country,is_eligible_search,is_eligible_checkout,is_ads_eligible
SKU-001,Trail Running Shoe,Lightweight trail shoe for daily runs,https://example.com/products/sku-001,Acme,https://example.com/images/sku-001.jpg,89.00 USD,in_stock,Acme,https://example.com,https://example.com/returns,US,US,true,false,true
SKU-002,Waterproof Shell,Packable waterproof jacket,https://example.com/products/sku-002,Acme,https://example.com/images/sku-002.jpg,149.00 USD,in_stock,Acme,https://example.com,https://example.com/returns,US,US,true,false,true

Include every base field marked Required in the product file schema. Set is_ads_eligible to true for products you want processed for ads, and keep their prices and availability current. Product pages and images must be publicly accessible HTTPS URLs.

For required fields, optional attributes, and variant grouping, follow the product file schema.

4. Upload the file

Connect to the returned SFTP location and upload your CSV. For example, after connecting with your SFTP client, upload a local file named catalog.csv:

put /path/to/catalog.csv catalog.csv

Place feed files directly in the SFTP root, without nested folders. If you split a catalog across files, the files should collectively represent the catalog, with each product appearing once.

Processing is asynchronous. A successful file transfer confirms that the file was uploaded; use Monitoring Uploads to check ingestion and identify problems.

Retrieve your feeds

List the feeds in your account. Request product_count to include the current count of ingested products marked as eligible for ads.

curl -G "https://api.ads.openai.com/v1/feeds" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  --data-urlencode 'include[]=product_count'

The count can remain zero while the initial upload is processing.

Manage SFTP access

Use these endpoints with no request body to pause or reactivate SFTP access:

ActionEndpoint
Pause SFTP accessPOST /v1/feeds/{feed_id}/sftp_access/pause
Reactivate SFTP accessPOST /v1/feeds/{feed_id}/sftp_access/activate

To pause ad delivery, use the campaign, ad group, or ad pause endpoint. SFTP access controls the upload connection.

Monitoring Uploads

Use upload history to check whether a product file was processed, how many rows were accepted or rejected, and which problems need attention.

Retrieve upload history

curl -G "https://api.ads.openai.com/v1/feeds/uploads" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}"

Find the upload for your feed and check its status and diagnostics. This abbreviated upload record shows rejected rows:

{
  "feed_id": "fd_123",
  "upload_id": "sftp_upload_123",
  "status": "completed_with_errors",
  "rows_accepted": 1245,
  "rows_rejected": 3,
  "rows_ads_eligible": 1245,
  "diagnostics": [
    {
      "code": "invalid_value",
      "severity": "warning",
      "field": "price",
      "rows_affected": 3
    }
  ]
}

This example reports invalid prices in three rows. Correct those values in your catalog and upload it again.

Understand upload statuses

StatusWhat to do
scanning, received, processingProcessing is still underway. Check again for a terminal status and available counts.
completedProcessing completed. Check counts and inspect the products you intend to advertise.
completed_with_errorsSome data was processed, but errors remain. Review diagnostics and rejected rows.
skippedThe upload was not processed. Check the source files and upload history before assuming products changed.
failedProcessing failed. Review diagnostics and correct the issue before uploading again.

A completed upload does not establish that every product can serve. Product data, availability, reviews, product filters, and campaign eligibility still apply.

Upload counts describe one upload and can differ from the feed’s current product count.

Resolve diagnostics

CodeWhat to check
missing_required_columnCompare the file’s columns with the product file schema and add the missing required column.
invalid_valueCheck the reported field’s formatting and allowed values. For example, verify price amounts and currency codes.
unsupported_file_typeCheck that the uploaded files use a supported format. This walkthrough uses CSV.
invalid_sftp_directory_layoutPlace feed files directly in the SFTP root and remove nested folders.

Updating Feeds & Delta API

Keep the feed current as prices, stock, and product details change. Choose an update method based on what changed.

ChangeMethod
Change an existing item’s title, price, or availabilityDelta API
Add new productsUpload an updated catalog over SFTP
Change other product fields, such as images or descriptionsUpload an updated catalog over SFTP
Refresh the complete catalogUpload an updated catalog over SFTP

Update existing products with the Delta API

You need an existing feed, an initial catalog that has been processed, and the product and variant identifiers from that catalog. The account must have access to manage feed data.

Send only the fields that changed. This example lowers one product’s price to $79.99 USD and marks another product out of stock:

curl -X PATCH "https://api.ads.openai.com/v1/feeds/fd_123/products" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
      {
        "id": "SKU-001",
        "variants": [
          {
            "id": "SKU-001",
            "price": {
              "amount": 7999,
              "currency": "USD"
            }
          }
        ]
      },
      {
        "id": "SKU-002",
        "variants": [
          {
            "id": "SKU-002",
            "availability": {
              "status": "out_of_stock"
            }
          }
        ]
      }
    ]
  }'

Example response:

{
  "id": "fd_123",
  "accepted": true
}

An accepted: true response confirms submission. Changes apply asynchronously, so allow time for product data and serving eligibility to update. See the Delta Feeds API for the full request and response details.

Use the correct identifiers

FieldValue
Feed ID in the URLThe feed_id returned when the feed was created.
products[].idThe catalog’s group_id when variants are grouped; otherwise the item_id.
products[].variants[].idThe individual variant’s item_id.

The CSV example without variant grouping uses SKU-001 as both the product ID and variant ID. Use your catalog’s identifiers when making updates.

Price and availability formats

The price format in the API differs from the CSV format:

SurfaceExampleMeaning
CSV price79.99 USDMajor currency units and a currency code.
Delta API price{"amount": 7999, "currency": "USD"}Integer minor currency units and a currency code.
Campaign budgets and bids50000000 micros50 major currency units.

For JPY, which has no decimal subdivision, a delta amount of 7999 represents ¥7,999. Do not use campaign budget micros for product prices.

Use status: "out_of_stock" to mark an item unavailable, and status: "in_stock" when it becomes available again.

An out-of-stock product stops qualifying for delivery after the change propagates. Returning it to stock makes it eligible for consideration once processing completes and the other serving requirements are met.

Update the catalog over SFTP

Upload an updated catalog to the same SFTP location used during setup.

  1. Keep the identifiers of existing products unchanged.
  2. Include your complete current catalog, including products you want to mark out of stock.
  3. Replace the existing file or files and remove outdated files. Reusing the same filenames makes recurring uploads easier to manage.
  4. If the catalog spans multiple files, include each product once across the files.
  5. Check upload history and inspect the resulting products.

To stop serving an unavailable product, explicitly set availability to out_of_stock. When it becomes available again, submit in_stock with its current details.

Keep your source catalog consistent with changes sent through the Delta API so later uploads contain the intended values. Use product queries to inspect titles and prices after processing.

Archive an unused feed

Archive a feed when it is no longer needed:

curl -X POST "https://api.ads.openai.com/v1/feeds/fd_123/archive" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}"

A feed cannot be archived while it is attached to a non-archived campaign or ad group. Archive those resources first; pausing them is not sufficient.

Product Sets & Filters

A product set selects which products an ad group can use from its campaign’s feed. Use filters to select a brand, category, price range, or another supported attribute.

The campaign owns the feed selection. An ad group inherits that feed. If you omit product_set, the ad group uses the campaign’s feed without additional product filters.

Preview matching products

Before creating or updating an ad group, query the feed with the filters you plan to use. This request selects Acme products priced at 100 major currency units or less:

curl -X POST "https://api.ads.openai.com/v1/feeds/fd_123/products/query" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      {
        "field": "brand",
        "operator": "in",
        "values": ["Acme"]
      },
      {
        "field": "price",
        "operator": "lte",
        "values": ["100"]
      }
    ],
    "limit": 20
  }'

Each product must satisfy the filters together. In the sample USD catalog, this selects the $89 trail shoe and excludes the $149 jacket. To inspect the feed without additional filters, send "filters": [].

Example response, with product fields abbreviated:

{
  "object": "list",
  "data": [
    {
      "item_id": "SKU-001",
      "brand": "Acme",
      "title": "Trail Running Shoe",
      "price": "89.00 USD"
    }
  ],
  "total_count": 2,
  "matched_count": 1
}

Review the matching products before saving the filters. Matching a filter does not guarantee that a product will receive impressions.

Choose fields and operators

Each filter contains a field, an operator, and a values array. Send values as strings, including numeric values such as "100" or "4.5".

You can select products by attributes such as brand, category, price, and item ID. For example, use in to select specific brands or lte to set an upper price bound, as shown above. Feed-defined metadata can also be used through ads_metadata.<field> when supported by your integration.

See the Ad Groups reference for supported filter fields, operators, and validation rules.

Apply the product set

Use the same filters in the ad group’s product_set. The product_feed_id must match the campaign’s feed.

For a new ad group, include this object in the create request shown in Product Feed Campaigns. For an existing ad group, update it with the complete desired product set:

curl -X POST "https://api.ads.openai.com/v1/ad_groups/adgrp_123" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_set": {
      "product_feed_id": "fd_123",
      "filters": [
        {
          "field": "brand",
          "operator": "in",
          "values": ["Acme"]
        },
        {
          "field": "price",
          "operator": "lte",
          "values": ["100"]
        }
      ]
    }
  }'

Treat the filters in an update as the complete desired list. Include filters you want to keep. To remove product filters, send the same product_feed_id with "filters": [].

Retrieve the ad group to confirm the saved product selection.

If no products match

Query with an empty filter list first. If products appear, add your filters one at a time to find the condition excluding the expected products. If the unfiltered result is empty, check upload history.

Catalog updates can change which products satisfy a filter. Review the selection after changes to prices, brands, categories, or other filtered attributes.

Product Feed Campaigns

Create a campaign linked to your feed, an ad group with a product selection, and a product-ad template. The template uses product text, prices, images, and destination URLs from the feed.

LevelWhat you configure
CampaignProduct feed, objective, budget, schedule, and targeting.
Ad groupBid configuration and optional product filters.
AdProduct-ad template.

Before you start

You will need a feed belonging to the same ad account as the campaign. Confirm that ingestion has completed for the products you intend to use and that your product filters match them.

This example creates a clicks campaign with a fixed bid. The $50 daily budget and $2 maximum bid are illustrative amounts for a USD account. Use amounts appropriate to your account’s currency and advertising plan.

Campaigns, ad groups, and ads are created paused so you can inspect them before activation. Reuse each creation request’s Idempotency-Key only when retrying that same request; use a new key for a new resource.

1. Create the campaign

Set mode to product_feed and product_feed_id to your feed ID. Use country-level targeting for this walkthrough.

curl -X POST "https://api.ads.openai.com/v1/campaigns" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Idempotency-Key: product-feed-campaign-001" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Spring catalog campaign",
    "status": "paused",
    "mode": "product_feed",
    "product_feed_id": "fd_123",
    "bidding_type": "clicks",
    "budget": {
      "daily_spend_limit_micros": 50000000
    },
    "targeting": {
      "locations": {
        "countries": ["US"]
      }
    }
  }'

Save the returned campaign id and use it in place of cmpn_123 below.

The campaign’s mode, objective, and linked feed cannot be changed after creation. Choose them before creating the campaign. Geographic targeting options for product-feed campaigns can differ from other campaign types; use the targeting options available to your account.

For campaign fields and lifecycle operations, see the Campaigns reference.

2. Create the ad group

Set a fixed click bid and attach the filters you previewed. This example selects Acme products priced at 100 major currency units or less.

curl -X POST "https://api.ads.openai.com/v1/ad_groups" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Idempotency-Key: product-feed-ad-group-001" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "cmpn_123",
    "name": "Acme products up to 100",
    "status": "paused",
    "bidding_config": {
      "billing_event_type": "click",
      "strategy": "fixed_bid",
      "max_bid_micros": 2000000
    },
    "product_set": {
      "product_feed_id": "fd_123",
      "filters": [
        {
          "field": "brand",
          "operator": "in",
          "values": ["Acme"]
        },
        {
          "field": "price",
          "operator": "lte",
          "values": ["100"]
        }
      ]
    }
  }'

Save the returned ad-group id and use it in place of adgrp_123 below.

Omit product_set if you want to use the campaign’s feed without additional product filters. If you supply it, its feed ID must match the campaign’s feed.

3. Create the product-ad template

Create an ad with creative.type set to product_ad_template:

curl -X POST "https://api.ads.openai.com/v1/ads" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Idempotency-Key: product-feed-ad-001" \
  -H "Content-Type: application/json" \
  -d '{
    "ad_group_id": "adgrp_123",
    "name": "Acme product template",
    "status": "paused",
    "creative": {
      "type": "product_ad_template",
      "title": "{{product.title}}",
      "body": "{{product.body}}",
      "price": "{{product.price}}"
    }
  }'

Save the returned ad id and use it in place of ad_123 below.

The template uses these macros:

MacroValue supplied from the product
{{brand}}Brand.
{{product.title}}Title.
{{product.body}}Description text.
{{product.price}}Price.

Use the body and price macros as shown in the example. You can combine supported macros with text in the title, such as {{brand}}: {{product.title}}. See the Ads reference for template requirements.

The selected product supplies the image and destination URL, so this example does not upload a separate creative image or set a target_url. Each product-feed ad group can contain at most one non-archived product-ad template.

To add tracking parameters to product URLs, use landing_page_configuration on the campaign, ad group, or ad. See the Campaigns reference.

4. Inspect before activation

Check:

  • The account’s status and reviews.
  • The campaign’s feed, objective, budget, and targeting.
  • The ad group’s bid configuration and matching products.
  • Product titles, descriptions, prices, images, and destination URLs.
  • The ad’s review status and any serving issues.

Request serving issues when retrieving the ad:

curl -G "https://api.ads.openai.com/v1/ads/ad_123" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  --data-urlencode 'include[]=serving_issues'

You can also request serving issues when retrieving the campaign or ad group.

5. Activate when ready

Activate the ad, ad group, and campaign:

curl -X POST "https://api.ads.openai.com/v1/ads/ad_123/activate" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}"
curl -X POST "https://api.ads.openai.com/v1/ad_groups/adgrp_123/activate" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}"
curl -X POST "https://api.ads.openai.com/v1/campaigns/cmpn_123/activate" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}"

Confirm each response reports status: "active". Delivery can begin once reviews, product availability, budget, and other serving requirements are satisfied.

Use other objectives and bid strategies

Product-feed campaigns can use impressions, clicks, or conversions objectives. Conversion-optimized campaigns are billed per click and require exactly one active standard conversion event setting to be attached when the campaign is created. See Conversion-Optimized Campaigns for setup requirements.

Maximize Results availability depends on the account and campaign configuration. Review its requirements before choosing it for a product-feed campaign.

Monitor delivery

Use product-segmented Insights to see which products received impressions and clicks. See the Insights reference for reporting examples.

If delivery does not begin, check serving issues and confirm that available products match the ad group’s filters.