API reference

Every CartAmplify endpoint with request/response shapes, error codes, and tier gates.

Complete HTTP API reference. All endpoints are JSON-only and live under https://api.cartamplify.com.

For the integration walkthrough, see the API quickstart. For the conceptual model behind these endpoints, see Technical concepts.

Base URL & versioning#

All public endpoints are versioned under /v1.

https://api.cartamplify.com/v1/<endpoint>

Breaking changes go behind a new version prefix. Additive changes (new optional fields) ship under the current version without notice — clients should ignore unknown fields gracefully.

Authentication#

Every authenticated request needs a Bearer token:

Authorization: Bearer sk_live_...

Two key types:

  • sk_test_… — Sandbox traffic. Recorded but excluded from production analytics and training. Use during integration.
  • sk_live_… — Production traffic.

Manage keys at Dashboard → API Keys. Treat both as secrets — they grant full read/write access to your workspace. Never ship them in client-side bundles.

Errors#

All errors return a consistent envelope:

{
  "error": {
    "code": "error_code_string",
    "message": "Human-readable description",
    "details": "Optional additional context (string or object)"
  }
}

Status codes#

StatusMeaning
200Success.
201Resource created.
202Accepted (event ingestion is async).
204Success, no body (DELETE).
207Multi-status (bulk operations with mixed results).
400Validation error.
401Missing or invalid API key.
403Tier upgrade required, or resource frozen.
404Resource not found.
409Conflict (e.g. product or widget already exists).
429Monthly request quota exhausted.
500Internal server error. Retry with backoff.
503Component degraded. Retry with backoff.

Common error codes#

CodeSurfaceMeaning
unsupported_languageAnylanguageCode is not configured for this workspace.
widget_frozenSearch / Browse / RecommendationsWidget requires a higher tier.
rate_limit_exceededAnyMonthly quota exhausted (after the ~10% grace buffer).
product_limit_exceededCatalogSync would exceed the tier product ceiling.
no_active_syncCatalogSent page 2+ without a successful page 1.

Rate limits#

Each tier has a monthly request quota:

TierMonthly requestsOverage per 1k
Starter20,000$3.00
Growth100,000$2.00
Amplify500,000$1.50
EnterpriseUnlimited

A ~10% grace buffer applies before hard-blocking. Over the buffer, requests return 429 rate_limit_exceeded. Monitor Dashboard → Billing to track quota consumption.

Pagination conventions#

Two styles are supported wherever pagination applies. Pick one per session — don’t mix.

  • Offset-based — Send offset + pageSize. Response includes totalSize. Maximum offset is 10,000.
  • Token-based — Send pageToken from a previous response. Response includes nextPageToken (opaque string, null when exhausted).

Token-based is preferred for deep pagination — offset-based is convenient for “jump to page N” UIs but the 10k cap prevents very deep paging.


Endpoints#

POST /v1/user-events#

When to use: Record a single user event as it happens on your storefront. Call from your frontend or backend the moment the action occurs (product view, cart add, search, purchase).

Returns 202 Accepted immediately; processing is async.

Required on every event: eventType, eventTime (ISO 8601, within ±24h of server time), visitorId, languageCode, userInfo.ipAddress, userInfo.userAgent.

Event-type-specific required fields:

Event typeAdditional required fields
home-page-view
category-page-viewpageCategories (1–20 category-path strings using > separator, e.g. ["Clothing > Men"])
detail-page-viewproductDetails (exactly 1 product, no quantity)
add-to-cartproductDetails (1–50, with quantity)
shopping-cart-page-viewproductDetails (0–100, with quantity)
purchase-completeproductDetails (1–50, with quantity), purchaseTransaction
searchsearchQuery OR pageCategories

Example — purchase-complete:

{
  "eventType": "purchase-complete",
  "eventTime": "2026-05-27T10:30:00Z",
  "visitorId": "visitor_abc123",
  "languageCode": "en",
  "userInfo": {
    "ipAddress": "203.0.113.45",
    "userAgent": "Mozilla/5.0...",
    "userId": "user_456"
  },
  "productDetails": [
    { "product": { "id": "SKU-12345" }, "quantity": 2, "attributionToken": "tok_xyz" }
  ],
  "purchaseTransaction": {
    "id": "TXN-2026-12345",
    "currencyCode": "USD",
    "revenue": "299.99",
    "tax": "25.00",
    "shipping": "10.00"
  },
  "cartId": "cart_xyz789"
}

Response 202:

{
  "status": "accepted",
  "eventId": "evt_abc123...",
  "message": "Event queued for processing"
}

Errors:

  • 400 unsupported_languagelanguageCode not configured.
  • 400 — Validation failure (missing required field, malformed payload).
  • 401 — Missing or invalid API key.

POST /v1/products/bulk#

When to use: Initial catalog import and scheduled full re-sync. For real-time changes (single product updated, marked out of stock, deleted), use the single-product endpoints below.

Paginated full sync. Up to 1000 products per page. Stateful across pages (see the bulk sync state machine).

Required per product: id, languageCode, title, uri, priceInfo.price, priceInfo.currencyCode, availability (IN_STOCK | OUT_OF_STOCK | PREORDER | BACKORDER).

Optional: description, categories[], brands[], tags[], sizes[], materials[], conditions[], images[], attributes[], availableQuantity, gtin, name, type, priceInfo.originalPrice, priceInfo.priceEffectiveTime, priceInfo.priceExpireTime, publishTime.

categories format: an array of category-path strings. Each path uses > (space-angle-space) as the hierarchy separator — e.g. "Clothing > Men > Shirts". A product can belong to multiple paths (e.g. ["Clothing > Men > Shirts", "Sale > Summer"]).

Request:

{
  "page": 1,
  "pages": 3,
  "products": [
    {
      "id": "SKU-12345",
      "languageCode": "en",
      "title": "Blue Cotton Shirt",
      "uri": "https://example.com/products/sku-12345",
      "categories": ["Clothing > Men > Shirts"],
      "brands": ["BrandCo"],
      "images": [
        { "uri": "https://example.com/img1.jpg", "width": 800, "height": 600 }
      ],
      "attributes": [
        { "key": "color", "value": { "text": ["blue"] } }
      ],
      "priceInfo": {
        "price": "29.99",
        "currencyCode": "USD",
        "originalPrice": "49.99"
      },
      "availability": "IN_STOCK",
      "availableQuantity": 15
    }
  ]
}

Sync flow:

  • Page 1 acquires the sync lock.
  • Pages 2..N-1 upsert.
  • Last page (page == pages) deletes products not touched, rebuilds the index, releases the lock.

Response 200 / 207:

{
  "success": true,
  "data": {
    "invalidProducts": {
      "SKU-99999": ["Field 'title' is required"]
    }
  }
}

Errors:

  • 400 no_active_sync — page 2+ sent without a successful page 1.
  • 403 product_limit_exceeded — tier ceiling exceeded.

POST /v1/products/{id}/{lang}#

When to use: A new product was created on your storefront — push it to CartAmplify immediately so it appears in search and recommendations within minutes. For the initial bulk import, use /v1/products/bulk instead.

Returns 409 if the product already exists at (item_id, language).

Path params: item_id (your SKU), language (ISO 639-1, lowercase).

Body: Same product schema as /v1/products/bulk (omit id and languageCode — taken from the path).

Response 201:

{ "success": true }

PUT /v1/products/{id}/{lang}#

When to use: A product changed — price update, stock change, new image, renamed category. Replaces the existing record. Returns 404 if the product doesn’t exist (use POST to create).

Same path params and body shape as the POST endpoint above.

Response 200:

{ "success": true }

DELETE /v1/products/{id}/{lang}#

When to use: A product was permanently removed from your storefront (not just out of stock — for that, PUT with availability: "OUT_OF_STOCK" instead).

Response 204: No body.


POST /v1/search#

When to use: Render the search results page. Call this from your storefront whenever a shopper submits a search query. Available on all tiers.

Required: visitorId, languageCode, eventTime, userInfo, widgetId, searchQuery.

Optional: pageSize (1–120, capped by widget config), offset (0–10000), pageToken, filters, orderBy, uri.

Request:

{
  "visitorId": "visitor_abc123",
  "languageCode": "en",
  "eventTime": "2026-05-27T10:30:00Z",
  "userInfo": {
    "ipAddress": "203.0.113.45",
    "userAgent": "Mozilla/5.0..."
  },
  "widgetId": "wgt_search_main",
  "searchQuery": "blue jeans",
  "pageSize": 20,
  "filters": {
    "categories": "Clothing",
    "brands": ["Levi's"],
    "availability": ["IN_STOCK"],
    "price": { "min": 20.0, "max": 150.0 },
    "attributes": [
      { "attribute": "size", "term": ["M", "L"], "queryType": "or" }
    ]
  },
  "orderBy": "popularity"
}

Response 200:

{
  "success": true,
  "data": {
    "results": [{ "id": "SKU-12345" }],
    "facets": {
      "size": {
        "key": "size",
        "values": [{ "value": "M", "count": "87" }]
      }
    },
    "attributes": [{ "key": "color", "values": [] }],
    "totalSize": 250,
    "attributionToken": "tok_xyz789",
    "nextPageToken": "tok_abc123",
    "latency": { "total_ms": 187.3 }
  }
}

results contains product IDs only — hydrate from your own catalog.

attributionToken should be threaded through subsequent detail-page-view, add-to-cart, and purchase-complete events.

Errors:

  • 400 — Invalid widget / unsupported language / malformed filter.
  • 403 widget_frozen — Tier-gated widget on a plan that doesn’t support it.
  • 429 rate_limit_exceeded — Monthly quota exhausted.

POST /v1/browse#

When to use: Render a category landing page or “Shop All”. Same request and response shape as /v1/search, with pageCategories[] instead of searchQuery.

Requires Growth tier or higher. Starter accounts receive 403 widget_frozen.

Required: visitorId, languageCode, eventTime, userInfo, widgetId, pageCategories.

pageCategories format: an array of category-path strings using the same > separator as the product categories field — entries are matched against categories by exact string overlap. Pass one entry to scope the browse to a single path; pass several to scope to any of them. E.g. ["Clothing > Men"], or ["Clothing > Men > Shirts", "Clothing > Men > T-shirts"].

Request:

{
  "visitorId": "visitor_abc123",
  "languageCode": "en",
  "eventTime": "2026-05-27T10:30:00Z",
  "userInfo": { "ipAddress": "...", "userAgent": "..." },
  "widgetId": "wgt_browse_main",
  "pageCategories": ["Clothing > Men"],
  "pageSize": 24
}

Response: Same shape as /v1/search.

Errors:

  • 403 — Starter tier.
  • 400 — Invalid widget / categories.

POST /v1/recommendations#

When to use: Fetch product recommendations for a placement — homepage shelf, PDP “Similar items”, cart-page cross-sell. The userEvent provides context: for a PDP, pass a detail-page-view with the current product; for a homepage shelf, pass a home-page-view.

Required: widgetId, userEvent (a complete user event object).

Request:

{
  "widgetId": "wgt_reco_pdp",
  "userEvent": {
    "visitorId": "visitor_abc123",
    "eventTime": "2026-05-27T10:30:00Z",
    "languageCode": "en",
    "userInfo": {
      "ipAddress": "203.0.113.45",
      "userAgent": "Mozilla/5.0...",
      "userId": "user_456"
    },
    "eventType": "detail-page-view",
    "productDetails": [
      { "product": { "id": "SKU-12345" } }
    ]
  }
}

Response 200:

{
  "success": true,
  "data": {
    "results": [{ "id": "SKU-12346" }, { "id": "SKU-12347" }],
    "attributionToken": "tok_abc123"
  }
}

Errors:

  • 403 — Widget type requires a higher tier.
  • 400 — Widget not found.
  • 404 — Widget deleted.

POST /v1/recommendations/create#

When to use: Create a recommendation widget programmatically. Most users create widgets through the dashboard UI; use the API when scripting workspace setup or managing widgets at scale.

Required: model, name (1–200 chars, unique per workspace), page (product_page or cart_page).

Models: similar_items, recommended_for_you, buy_it_again, others_you_may_like, frequently_bought_together.

Request:

{
  "model": "similar_items",
  "name": "Similar Products",
  "page": "product_page"
}

Response 201:

{
  "success": true,
  "widget_id": "wgt_xyz789abc",
  "message": "Widget created successfully"
}

Errors:

  • 403 — Model requires a higher tier, or widget count limit reached.
  • 409 — Name already in use.

GET /v1/settings#

When to use: Discover the active widget configuration for a workspace from a client (e.g. the WordPress plugin querying which widgets to render). Cached for 1 hour.

Response 200:

{
  "data": {
    "widgets": [
      {
        "widgetId": "wgt_search_main",
        "type": "search",
        "name": "Main Search",
        "active": true
      },
      {
        "widgetId": "wgt_reco_pdp",
        "type": "recommendation",
        "name": "Similar Products",
        "active": true,
        "model": "similar_items",
        "destination": "product-page"
      }
    ]
  }
}

Reference tables#

Filter operators#

Used in filters.attributes[] in /v1/search and /v1/browse:

queryTypeBehavior
or (default)Product matches if any term matches the attribute value.
andProduct matches only if all terms match.

Other filter fields (categories, brands, availability, price) are OR/range semantics — see Search for usage notes.

Sort orders (orderBy)#

ValueBehavior
(omitted)Default semantic + personalized relevance.
ratingHighest average rating first.
ratingCountMost-reviewed first.
priceCheapest first.
price descMost expensive first.
popularity7-day weighted activity: views + 2×ATC + 5×purchases.

Tier gates summary#

FeatureStarterGrowthAmplifyEnterprise
Product limit5005,00050,00050,000
Monthly API requests20k100k500kUnlimited
Widget count2515Unlimited
Browse AI
Recommendation modelssimilar_items, recommended_for_you+ buy_it_again, others_you_may_like+ frequently_bought_togetherAll
Merchandising rules
Browse analytics