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"
}| Field | Type | Description |
|---|---|---|
id | string | Unique product identifier |
title | string | Product name (60 chars recommended) |
description | string | Detailed description |
price | integer | Price in smallest currency unit (cents) |
currency | string | ISO 4217 currency code |
availability | string | Stock status |
Recommended Fields
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:
| State | Meaning | AI Behavior |
|---|---|---|
in_stock | Available now | Can add to cart |
low_stock | Limited quantity | May warn user |
out_of_stock | Not available | Won’t recommend |
preorder | Future availability | Will note ship date |
discontinued | No longer sold | Won’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_12345Best 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:
- Implementing Checkout API - Enable purchases
- Handling Payment Tokens - Process payments
- Testing Your Integration - Verify everything works