Skip to content

Search API ​

Reference for the Search API. One request takes a product query and returns a ranked list of US products, each with offers from the retailers that sell it. It does not hold a conversation; use the shopping_agent tool for comparison, product Q&A, and follow-ups.

Endpoint ​

ItemValue
RESTPOST https://commerceai.ai/api/v1/catalog/search with a JSON body
AuthAuthorization: Bearer <YOUR_API_KEY> on every request. The same API keys work for Search and shopping_agent.
MCPNot available. Search is REST only; shopping_agent is the only MCP tool.
Price5 credits per request. See Price.

Quickstart ​

Set your API key, then send one query. Each example costs 5 credits.

bash
export COMMERCEAI_API_KEY="<YOUR_API_KEY>"

cURL ​

bash
curl https://commerceai.ai/api/v1/catalog/search \
  -H "Authorization: Bearer $COMMERCEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "wireless earbuds", "filters": {"price_max": 50}, "limit": 5}'

Python ​

Requires Python 3.9+ and requests.

bash
pip install requests
python
import os
import requests

response = requests.post(
    "https://commerceai.ai/api/v1/catalog/search",
    headers={"Authorization": f"Bearer {os.environ['COMMERCEAI_API_KEY']}"},
    json={"query": "wireless earbuds", "filters": {"price_max": 50}, "limit": 5},
    timeout=30,
)
data = response.json()
if not response.ok:
    raise SystemExit(f"{data.get('error') or data.get('code')}: {data['message']}")

for product in data["products"]:
    cheapest = product["offers"][0]
    print(product["title"], cheapest["price"], cheapest["source_site"], cheapest.get("buy_url"))
print("credits used:", data["usage"]["credits_used"])

JavaScript ​

Requires Node.js 18+ (built-in fetch). No package to install.

js
const response = await fetch('https://commerceai.ai/api/v1/catalog/search', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.COMMERCEAI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ query: 'wireless earbuds', filters: { price_max: 50 }, limit: 5 }),
  signal: AbortSignal.timeout(30_000),
})
const data = await response.json()
if (!response.ok) throw new Error(`${data.error ?? data.code}: ${data.message}`)

for (const product of data.products) {
  const [cheapest] = product.offers
  console.log(product.title, cheapest.price, cheapest.source_site, cheapest.buy_url)
}
console.log('credits used:', data.usage.credits_used)

Request ​

ParameterTypeRequiredDescription
querystringYesProduct keywords or a short description, 1 to 200 characters. Leading and trailing spaces are trimmed.
filtersobjectNoNarrows the results. See Filters.
limitintegerNoNumber of products to return, 1 to 20. Default 20. There is no next page.
offer_limitintegerNoOffers per product, 1 to 20. Default 3. Each retailer contributes at most its cheapest offer.
marketstringNoDefault US. Any other market returns outcome: "UNSUPPORTED" without searching and is not charged.
request_idstringNoYour own UUID for this request. The server generates one if omitted. Reusing an id returns IDEMPOTENCY_CONFLICT.

Unknown parameters are rejected with INVALID_ARGUMENT.

Filters ​

FilterTypeDescription
price_minnumberLowest price in USD. Must be positive and not above price_max.
price_maxnumberHighest price in USD. Must be positive.
websitesstring[]Only return offers from these retailers: amazon, walmart, macys. Any other value is rejected.
brandsstring[]Brands to prefer. At most 20, each at most 64 characters.

Unknown filters are rejected with INVALID_ARGUMENT.

Response ​

FieldTypeDescription
api_versionstringAlways v1.
request_idstringYour request_id, or the one the server generated. Quote it when you contact support.
outcomestringSUCCESS: products found. NO_RESULTS: nothing matched; products is empty. UNSUPPORTED: the market is not supported; nothing was searched.
applied_filtersobjectThe filters the search used, after trimming and de-duplication.
productsobject[]Matching products, best match first. See Product.
usageobjectcredits_used: credits this request used.
trace_idstringPresent on some responses. Quote it with request_id when you contact support.

Product ​

FieldTypeDescription
titlestringProduct name.
brandstringBrand, when known.
categorystringCategory, when known.
image_urlstringMain image, when available.
price_min, price_maxnumberLowest and highest offer price, in currency.
currencystringUSD.
availabilitystringin_stock, out_of_stock, or unknown.
rating, review_countnumberRating and number of reviews, when available.
offer_countintegerNumber of offers returned for this product.
offersobject[]Offers sorted by price, lowest first. See Offer.

Fields with no value are omitted rather than sent as null. Products and offers can carry more fields than listed here, such as description, gtin, image_urls, specs, and reviews; ignore fields you do not use.

Offer ​

FieldTypeDescription
source_sitestringRetailer, lowercase, for example walmart.
merchant_namestringSeller name, when it differs from the retailer.
pricenumberPrice in currency.
currencystringUSD.
availabilitystringin_stock, out_of_stock, or unknown.
buy_urlstringLink to the product page on the retailer's site.
updated_atintegerWhen the price was last checked, in Unix seconds.

Billing ​

ResultCredits
SUCCESS5
NO_RESULTS5
UNSUPPORTED market0
Request that ends in an error0

Search requests are billed separately from shopping_agent. Searches the agent runs inside a turn are billed as its tool calls, not as Search API requests.

Errors ​

Errors return an HTTP status and this body:

json
{
  "error": "INVALID_ARGUMENT",
  "message": "invalid argument",
  "request_id": "0192f1c4-7b6e-7c3a-9d2e-5f4a1b2c3d4e",
  "retryable": false
}
HTTPerrorRetry
400INVALID_ARGUMENTNo. Fix the request.
401UNAUTHENTICATEDNo. The API key is missing, invalid, or deleted. The response also carries a WWW-Authenticate: Bearer header.
403INSUFFICIENT_CREDITS, ACCOUNT_DISABLED, FORBIDDENNo. Top up or contact support.
409IDEMPOTENCY_CONFLICTNo. Send a new request_id.
429RATE_LIMITED, SHARED_CAPACITYYes, after the Retry-After header.
502UPSTREAM_ERRORNo.
503SERVICE_UNAVAILABLEYes, with backoff.
504UPSTREAM_TIMEOUTYes, with backoff.

Retry only when retryable is true. Do not parse message; it can change.

Limits ​

Search has its own rate limits, separate from the shopping_agent limits. Requests over the limit return HTTP 429 RATE_LIMITED with a Retry-After header in seconds. Need higher limits? Email support@commerceai.ai.

Next ​