Skip to Content
🚀Agentic Commerce Protocol is now live! Instant Checkout is available in ChatGPT. Learn more →
DocumentationStep-by-Step TutorialsBuilding a Product Feed

Building a Product Feed

The Product Feed is how AI agents discover your products. A well-structured feed enables better discovery, more accurate recommendations, and higher conversion rates.

Product Feed Specification

Required Fields

Every product must have these fields:

{ "id": "prod_12345", "title": "Wireless Bluetooth Headphones", "description": "Premium over-ear headphones with active noise cancellation...", "price": 19999, "currency": "USD", "availability": "in_stock" }
FieldTypeDescription
idstringUnique product identifier
titlestringProduct name (60 chars recommended)
descriptionstringDetailed description
priceintegerPrice in smallest currency unit (cents)
currencystringISO 4217 currency code
availabilitystringStock status

These fields improve AI understanding:

{ "id": "prod_12345", "title": "Wireless Bluetooth Headphones", "description": "Premium over-ear headphones...", "price": 19999, "currency": "USD", "availability": "in_stock", "category": "Electronics > Audio > Headphones", "brand": "SoundMax", "sku": "SM-WBH-001", "images": [ "https://cdn.example.com/headphones-main.jpg", "https://cdn.example.com/headphones-side.jpg" ], "url": "https://example.com/products/wireless-headphones", "rating": { "average": 4.6, "count": 2547 }, "attributes": { "color": "Black", "battery_life": "30 hours", "connectivity": "Bluetooth 5.2", "driver_size": "40mm" } }

Product Variants

For products with options (size, color):

{ "id": "prod_tshirt", "title": "Classic Cotton T-Shirt", "price": 2499, "currency": "USD", "availability": "in_stock", "variants": [ { "id": "var_tshirt_s_black", "title": "Small / Black", "attributes": { "size": "S", "color": "Black" }, "price": 2499, "availability": "in_stock", "sku": "TSHIRT-S-BLK" }, { "id": "var_tshirt_m_black", "title": "Medium / Black", "attributes": { "size": "M", "color": "Black" }, "price": 2499, "availability": "in_stock", "sku": "TSHIRT-M-BLK" }, { "id": "var_tshirt_l_black", "title": "Large / Black", "attributes": { "size": "L", "color": "Black" }, "price": 2499, "availability": "low_stock", "sku": "TSHIRT-L-BLK" } ] }

Building the Feed Endpoint

Basic Implementation

// routes/products.js const express = require('express'); const router = express.Router(); // GET /acp/v1/products router.get('/products', async (req, res) => { try { const { // Pagination limit = 100, offset = 0, // Filtering category, brand, min_price, max_price, availability, // Search search, // Sorting sort_by = 'title', sort_order = 'asc' } = req.query; // Build query (pseudocode - adapt to your database) let query = db.products.find(); // Apply filters if (category) { query = query.where('category').regex(new RegExp(category, 'i')); } if (brand) { query = query.where('brand').equals(brand); } if (min_price) { query = query.where('price').gte(parseInt(min_price)); } if (max_price) { query = query.where('price').lte(parseInt(max_price)); } if (availability) { query = query.where('availability').equals(availability); } if (search) { query = query.where('$text').search(search); } // Apply sorting const sortDir = sort_order === 'desc' ? -1 : 1; query = query.sort({ [sort_by]: sortDir }); // Get total count before pagination const total = await query.clone().countDocuments(); // Apply pagination const products = await query .skip(parseInt(offset)) .limit(parseInt(limit)) .exec(); res.json({ products, pagination: { total, limit: parseInt(limit), offset: parseInt(offset), has_more: parseInt(offset) + products.length < total } }); } catch (error) { console.error('Product feed error:', error); res.status(500).json({ error: 'Failed to fetch products' }); } }); // GET /acp/v1/products/:id router.get('/products/:id', async (req, res) => { try { const product = await db.products.findOne({ id: req.params.id }); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.json(product); } catch (error) { console.error('Product fetch error:', error); res.status(500).json({ error: 'Failed to fetch product' }); } }); module.exports = router;

Advanced Search Implementation

Enable natural language search for AI agents:

// services/productSearch.js class ProductSearchService { constructor(products) { this.products = products; } search(query, options = {}) { const { limit = 20, filters = {} } = options; // Tokenize and normalize query const tokens = this.tokenize(query); // Score each product const scored = this.products.map(product => ({ product, score: this.calculateScore(product, tokens, filters) })); // Filter out zero scores and sort return scored .filter(item => item.score > 0) .sort((a, b) => b.score - a.score) .slice(0, limit) .map(item => item.product); } tokenize(query) { return query .toLowerCase() .replace(/[^\w\s]/g, '') .split(/\s+/) .filter(token => token.length > 2); } calculateScore(product, tokens, filters) { let score = 0; // Apply hard filters first if (filters.min_price && product.price < filters.min_price) return 0; if (filters.max_price && product.price > filters.max_price) return 0; if (filters.category && !product.category.toLowerCase().includes(filters.category.toLowerCase())) return 0; // Score title matches (highest weight) const titleLower = product.title.toLowerCase(); for (const token of tokens) { if (titleLower.includes(token)) { score += 10; // Bonus for word boundary match if (titleLower.split(/\s+/).includes(token)) { score += 5; } } } // Score description matches const descLower = product.description.toLowerCase(); for (const token of tokens) { if (descLower.includes(token)) { score += 2; } } // Score category matches const catLower = product.category.toLowerCase(); for (const token of tokens) { if (catLower.includes(token)) { score += 5; } } // Score brand matches if (product.brand) { const brandLower = product.brand.toLowerCase(); for (const token of tokens) { if (brandLower.includes(token)) { score += 8; } } } // Boost by rating if (product.rating?.average) { score *= (1 + (product.rating.average - 3) * 0.1); } // Penalize out of stock if (product.availability !== 'in_stock') { score *= 0.5; } return score; } } module.exports = ProductSearchService;

Feed Optimization for AI Agents

Write AI-Friendly Descriptions

AI agents parse your descriptions to understand products. Make them clear and informative:

// ❌ Bad description { "description": "Great headphones! You'll love them. Best in class!!!" } // ✅ Good description { "description": "Over-ear wireless headphones with active noise cancellation. Features 40mm drivers for rich audio, 30-hour battery life, Bluetooth 5.2 connectivity, and USB-C fast charging. Includes carrying case and 3.5mm audio cable. Compatible with iOS and Android. Weight: 250g." }

Use Structured Attributes

Attributes help AI agents filter and compare:

{ "attributes": { "material": "100% cotton", "care": "Machine wash cold", "origin": "Made in USA", "weight": "5.3 oz", "fit": "Regular fit", "features": ["Pre-shrunk", "Double-stitched hem", "Tagless label"] } }

Include Semantic Categories

Use hierarchical categories that AI can parse:

{ "category": "Electronics > Computers > Laptops > Gaming Laptops", "tags": ["gaming", "laptop", "high-performance", "rtx-4090", "portable"] }

Availability States

Use clear availability states:

StateMeaningAI Behavior
in_stockAvailable nowCan add to cart
low_stockLimited quantityMay warn user
out_of_stockNot availableWon’t recommend
preorderFuture availabilityWill note ship date
discontinuedNo longer soldWon’t show
{ "availability": "low_stock", "availability_quantity": 3, "availability_note": "Only 3 left!" }

Handling Large Catalogs

Pagination

For large catalogs, implement proper pagination:

// Request GET /acp/v1/products?limit=50&offset=100 // Response { "products": [...], "pagination": { "total": 5000, "limit": 50, "offset": 100, "has_more": true, "next_offset": 150 } }

Incremental Updates

For AI agents that cache your feed, provide update timestamps:

{ "products": [...], "feed_info": { "last_updated": "2024-12-15T10:30:00Z", "update_frequency": "hourly", "total_products": 5000 } }

Testing Your Product Feed

Validate JSON structure

curl http://localhost:3000/acp/v1/products | jq .

Test search functionality

# Search by term curl "http://localhost:3000/acp/v1/products?search=wireless+headphones" # Filter by price curl "http://localhost:3000/acp/v1/products?min_price=5000&max_price=20000" # Filter by category curl "http://localhost:3000/acp/v1/products?category=Electronics"

Test pagination

# First page curl "http://localhost:3000/acp/v1/products?limit=10&offset=0" # Second page curl "http://localhost:3000/acp/v1/products?limit=10&offset=10"

Test single product

curl http://localhost:3000/acp/v1/products/prod_12345

Best Practices

âś…

Do:

  • Use consistent IDs that don’t change
  • Include all relevant attributes
  • Keep descriptions factual and detailed
  • Update availability in real-time
  • Use standard units (oz, cm, etc.)
⚠️

Don’t:

  • Use marketing fluff in descriptions
  • Include HTML in product data
  • Change product IDs
  • Leave required fields empty
  • Use inconsistent pricing formats

Next Steps

Now that your product feed is ready:

Specification maintained by OpenAI and Stripe

AboutPrivacyTermsRSS

Apache 2.0 · Open Source