Skip to content
New Webhooks added: Inventory and Order modifications. Check the changelog →
Cartly Developers

Agent Commerce API

Make your storefront machine-readable for AI shopping agents: the discovery manifest, llms.txt, robots.txt rules, and the anonymous, market-aware catalog read-API — endpoints, request parameters, and example responses.

Overview

Agent Commerce is the discoverability and read-API layer that lets AI shopping agents — assistants like ChatGPT, Claude, and Perplexity, and the shopping tools built on top of them — find, understand, and query a Cartly storefront's catalog without authentication. It is on by default for every shop and pairs with the storefront's existing Product/Collection JSON-LD (see below) and sitemap. A shop can opt out from Settings → Agent Commerce in the admin — see the Help Center article for the merchant-facing view.

Everything documented here is anonymous and read-only: there is no write path, no cart, and no checkout capability exposed to an agent.

Discovery: the Manifest and llms.txt

Every shop publishes a machine-readable manifest at GET /.well-known/agent-commerce.json (proxied from GET /shops/:shopId/agent/v1/manifest) describing the shop, its markets, its structured-data support, and its catalog endpoint:

{
  "spec": "cartly-agent-commerce/0.1",
  "agent_ready": true,
  "shop": {
    "name": "Example Store",
    "url": "https://your-store.mycartly.pro",
    "logo": "https://your-store.mycartly.pro/logo.png",
    "currency": "USD"
  },
  "markets": [
    { "code": "US", "currency": "USD", "locales": ["en"] }
  ],
  "structured_data": {
    "product_jsonld": true,
    "sitemap": "https://your-store.mycartly.pro/sitemap.xml"
  },
  "endpoints": {
    "catalog": "https://your-store.mycartly.pro/shops/<shop_id>/agent/v1"
  },
  "policies": {
    "returns": "https://your-store.mycartly.pro/pages/returns",
    "shipping": "https://your-store.mycartly.pro/pages/shipping",
    "terms": "https://your-store.mycartly.pro/terms",
    "privacy": "https://your-store.mycartly.pro/privacy"
  },
  "capabilities": { "browse": true, "price": true, "availability": true, "cart": false, "checkout": false },
  "usage": { "rate_limit": "60/min", "pii": "none" }
}

When a shop has opted out, the manifest still returns 200 (never a 404) with a minimal body — just spec, shop, and "agent_ready": false — so an agent learns "found, but disabled" and stops probing instead of retrying a missing endpoint.

A plain-language companion is published at GET /llms.txt, following the llms.txt convention: a short summary with links to the storefront, sitemap, catalog API, and published policies:

# Example Store

> Online store. Products, prices and availability are machine-readable.

## Links
- [Storefront](https://your-store.mycartly.pro)
- [Sitemap](https://your-store.mycartly.pro/sitemap.xml)
- [Catalog API](https://your-store.mycartly.pro/shops/<shop_id>/agent/v1)

## Policies
- [privacy](https://your-store.mycartly.pro/privacy)
- [returns](https://your-store.mycartly.pro/pages/returns)
- [shipping](https://your-store.mycartly.pro/pages/shipping)
- [terms](https://your-store.mycartly.pro/terms)

Both endpoints are cacheable (Cache-Control: public, max-age=300) and always return 200.

robots.txt Rules for AI Crawlers

Every storefront's /robots.txt carries an explicit User-agent block for the major AI crawlers — GPTBot, OAI-SearchBot, ChatGPT-User, PerplexityBot, Google-Extended, and ClaudeBot — separate from the wildcard rules general search engines see:

# AI agents (per-shop opt-out via agent_ready)
User-agent: GPTBot
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Google-Extended
Allow: /

User-agent: ClaudeBot
Allow: /

When the shop opts out, every AI-agent User-agent block flips to Disallow: / while the general User-agent: * block — the one search engines read — is untouched.

Rich Structured Data (JSON-LD)

Product pages emit one complete schema.org/Product node: name, description, image, brand, a per-variant Offer (SKU, GTIN, price, availability), an AggregateOffer spanning all variants, and an aggregateRating once the product has reviews. Collection pages emit an ItemList of their products; other pages emit a BreadcrumbList. This is the same structured data that powers search-engine rich results — AI agents parse it too.

Unlike the read-API below, JSON-LD emission is not gated by the Agent Commerce opt-out: a merchant who opts out still keeps full structured data for Googlebot and other search engines — opting out only turns off the AI-agent-specific surfaces (manifest, llms.txt, read-API, AI-crawler robots.txt rules).

The Anonymous Catalog Read-API

Base path: /shops/:shopId/agent/v1. No authentication or API key is required. Every endpoint below honors the shop's Agent Commerce opt-out: once a shop has opted out, every read-API request returns 403 with {"agent_ready": false}.

Method & PathDescription
GET /shops/:shopId/agent/v1/productsList published, active products
GET /shops/:shopId/agent/v1/products/:handleFull detail for one product
GET /shops/:shopId/agent/v1/collectionsList active collections
GET /shops/:shopId/agent/v1/collections/:handleOne collection with its products

List Products

GET /shops/:shopId/agent/v1/products

Query parameters:

  • market — a market country code; prices are returned in that market's presentment currency. Omit it to get the shop's primary market (or its base currency, if no market is configured)
  • limit (alias first) — page size, default 50, max 100
  • cursor (alias after) — opaque keyset cursor read from a previous response's next_cursor
  • collection — filter to one collection by handle
  • q — case-insensitive title search
GET /shops/<shop_id>/agent/v1/products?market=US&limit=2

{
  "products": [
    {
      "handle": "hydrating-serum",
      "title": "Hydrating Serum",
      "url": "https://your-store.mycartly.pro/products/hydrating-serum",
      "image": "https://your-store.mycartly.pro/media/serum.jpg",
      "brand": "Example Brand",
      "price": { "amount_minor": 2900, "amount": "29.00", "currency": "USD" },
      "price_range": {
        "low": { "amount_minor": 2900, "amount": "29.00", "currency": "USD" },
        "high": { "amount_minor": 3900, "amount": "39.00", "currency": "USD" }
      },
      "availability": "in_stock"
    }
  ],
  "next_cursor": "ODRhYjM4MDktNGRkYi00MTdiLThhZTAtNDkzZTQxMTJlOTE2"
}

price_range only appears when a product's variants span more than one distinct price; price is always the minimum. availability is one of in_stock, low_stock, or out_of_stock — Cartly never returns an exact quantity to an anonymous caller. The last page omits next_cursor (empty string).

Get a Product

GET /shops/:shopId/agent/v1/products/:handle

{
  "handle": "hydrating-serum",
  "title": "Hydrating Serum",
  "url": "https://your-store.mycartly.pro/products/hydrating-serum",
  "image": "https://your-store.mycartly.pro/media/serum.jpg",
  "brand": "Example Brand",
  "price": { "amount_minor": 2900, "amount": "29.00", "currency": "USD" },
  "availability": "in_stock",
  "description": "A lightweight, fast-absorbing serum...",
  "variants": [
    {
      "sku": "SER-30ML",
      "gtin": "0123456789012",
      "title": "30ml",
      "options": { "Size": "30ml" },
      "price": { "amount_minor": 2900, "amount": "29.00", "currency": "USD" },
      "availability": "in_stock",
      "url": "https://your-store.mycartly.pro/products/hydrating-serum?variant=<variant_id>"
    }
  ],
  "shipping": { "available": true, "region": "US" },
  "returns": { "url": "https://your-store.mycartly.pro/pages/returns" },
  "rating": { "value": 4.8, "count": 132 }
}

shipping, returns, and rating are each omitted entirely (not emitted as empty objects) when the shop has no active shipping zone, no published returns page, or the product has no reviews yet — an honest degrade rather than invented data.

List and Get Collections

GET /shops/:shopId/agent/v1/collections lists active collections (limit/first plus offset):

{
  "collections": [
    { "handle": "skincare", "title": "Skincare", "url": "https://your-store.mycartly.pro/collections/skincare" }
  ]
}

GET /shops/:shopId/agent/v1/collections/:handle returns one collection with its products, in the same flat shape as the products-list endpoint:

{
  "handle": "skincare",
  "title": "Skincare",
  "url": "https://your-store.mycartly.pro/collections/skincare",
  "products": [ { "handle": "hydrating-serum", "...": "..." } ]
}

Opt-Out, Rate Limits & Caching

  • Opt-out — when a merchant turns Agent Commerce off (Settings → Agent Commerce), every read-API request returns 403 {"agent_ready": false}. The discovery manifest still returns 200 with agent_ready: false, so an agent gets a definite answer instead of a broken lookup.
  • Rate limit — roughly 60 requests/min per caller, matching the usage.rate_limit field advertised in the manifest.
  • Caching — every read-API response sets Cache-Control: public, max-age=60, s-maxage=300; the manifest and llms.txt use public, max-age=300. Respect these headers rather than polling faster than necessary.