Public endpoints
What public endpoints are for
The 9 public storefront endpoints power the Sumeru Systems storefront widgets (Theme App Extension) and any custom storefront integration. They're:
- CORS-allowed for your shop's allow-listed domains
- No authentication required for default storefront use
- API-key authenticated for headless / custom storefronts
- Rate-limited per shop to prevent abuse
- Cached aggressively at edge for low-latency reads
Endpoint catalog
| Endpoint | Purpose | Method |
|---|---|---|
/api/public/reviews | Read reviews; submit new review | GET, POST |
/api/public/referral | Generate / validate referral codes | GET, POST |
/api/public/bundles | Fetch bundle definitions | GET |
/api/public/upsell | Get cart upsell suggestions | GET |
/api/public/quiz | Submit quiz answers; get results | POST |
/api/public/ab-experiment | Get variant assignment | GET |
/api/public/product-feed | Fetch product catalog (filtered) | GET |
/api/public/cart-nudge | Get personalized cart-page content | GET |
/api/public/pixel | Pixel event ingestion | POST |
Full OpenAPI spec at /developers/api/public-api/.
Auth model
Default: CORS allow-list
Each shop has a configured allow-list of domains
(yourshop.com, www.yourshop.com). Requests from those
origins succeed without explicit auth:
// From yourshop.com
fetch('https://api.sumeru.systems/api/public/reviews?product_id=prd_123', {
headers: { 'X-Shop-Domain': 'yourshop.com' }
})
.then(r => r.json())
API key (for headless / custom)
For storefronts not on standard Shopify domains:
X-Public-Key: pk_live_xY7aB3cKz9...
Public keys are scoped to public endpoints only — cannot access the admin API.
Caching
Read endpoints are cached at edge:
| Endpoint | Cache TTL |
|---|---|
/reviews | 60s |
/bundles | 5 min |
/upsell | 60s |
/product-feed | 5 min |
/cart-nudge | 60s (per cart) |
/ab-experiment | per session, immutable |
Cache busts on relevant admin updates. Headers include
Cache-Control and X-Cache: HIT|MISS.
CORS configuration
Default allow:
Origin: any allow-listed domainMethods: GET, POST, OPTIONSHeaders: Content-Type, X-Shop-Domain, X-Public-KeyCredentials: false (default; configurable)
Pre-flight (OPTIONS) responses cached at browser for 1h.
Rate limits
Public endpoints have higher limits than admin (storefront traffic patterns):
| Endpoint pattern | Per shop | Per IP |
|---|---|---|
| Read endpoints | 10K/min | 100/min |
/pixel (event ingest) | 100K/min | 1K/min |
/quiz, /referral | 1K/min | 20/min |
When limited, 429 with Retry-After.
Abuse prevention
- IP-level rate limits (above)
- Bot detection on submit endpoints (review, quiz)
- HMAC challenge for high-volume endpoints (
/pixel) - Per-shop daily quotas (resets midnight UTC)
Common patterns
Embed reviews on PDP
<div id="reviews"></div>
<script>
fetch('/api/public/reviews?product_id={{product.id}}')
.then(r => r.json())
.then(data => {
document.getElementById('reviews').innerHTML =
data.data.map(r => `<div>${r.rating}★ ${r.body}</div>`).join('');
});
</script>
Show cart upsell
async function loadUpsell(cartItems) {
const response = await fetch('/api/public/upsell', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items: cartItems }),
});
const { data } = await response.json();
return data.suggestions;
}
Submit quiz
async function submitQuiz(answers, email) {
return fetch('/api/public/quiz', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ answers, email }),
}).then(r => r.json());
}