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#
| Status | Meaning |
|---|---|
| 200 | Success. |
| 201 | Resource created. |
| 202 | Accepted (event ingestion is async). |
| 204 | Success, no body (DELETE). |
| 207 | Multi-status (bulk operations with mixed results). |
| 400 | Validation error. |
| 401 | Missing or invalid API key. |
| 403 | Tier upgrade required, or resource frozen. |
| 404 | Resource not found. |
| 409 | Conflict (e.g. product or widget already exists). |
| 429 | Monthly request quota exhausted. |
| 500 | Internal server error. Retry with backoff. |
| 503 | Component degraded. Retry with backoff. |
Common error codes#
| Code | Surface | Meaning |
|---|---|---|
unsupported_language | Any | languageCode is not configured for this workspace. |
widget_frozen | Search / Browse / Recommendations | Widget requires a higher tier. |
rate_limit_exceeded | Any | Monthly quota exhausted (after the ~10% grace buffer). |
product_limit_exceeded | Catalog | Sync would exceed the tier product ceiling. |
no_active_sync | Catalog | Sent page 2+ without a successful page 1. |
Rate limits#
Each tier has a monthly request quota:
| Tier | Monthly requests | Overage per 1k |
|---|---|---|
| Starter | 20,000 | $3.00 |
| Growth | 100,000 | $2.00 |
| Amplify | 500,000 | $1.50 |
| Enterprise | Unlimited | — |
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 includestotalSize. Maximum offset is 10,000. - Token-based — Send
pageTokenfrom a previous response. Response includesnextPageToken(opaque string,nullwhen 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 type | Additional required fields |
|---|---|
home-page-view | — |
category-page-view | pageCategories (1–20 category-path strings using > separator, e.g. ["Clothing > Men"]) |
detail-page-view | productDetails (exactly 1 product, no quantity) |
add-to-cart | productDetails (1–50, with quantity) |
shopping-cart-page-view | productDetails (0–100, with quantity) |
purchase-complete | productDetails (1–50, with quantity), purchaseTransaction |
search | searchQuery 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_language—languageCodenot 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:
queryType | Behavior |
|---|---|
or (default) | Product matches if any term matches the attribute value. |
and | Product 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)#
| Value | Behavior |
|---|---|
| (omitted) | Default semantic + personalized relevance. |
rating | Highest average rating first. |
ratingCount | Most-reviewed first. |
price | Cheapest first. |
price desc | Most expensive first. |
popularity | 7-day weighted activity: views + 2×ATC + 5×purchases. |
Tier gates summary#
| Feature | Starter | Growth | Amplify | Enterprise |
|---|---|---|---|---|
| Product limit | 500 | 5,000 | 50,000 | 50,000 |
| Monthly API requests | 20k | 100k | 500k | Unlimited |
| Widget count | 2 | 5 | 15 | Unlimited |
| Browse AI | — | ✓ | ✓ | ✓ |
| Recommendation models | similar_items, recommended_for_you | + buy_it_again, others_you_may_like | + frequently_bought_together | All |
| Merchandising rules | — | — | ✓ | ✓ |
| Browse analytics | — | — | ✓ | ✓ |