Bundles
The SDK works with two bundle shapes, both plain JSON returned by the AgentShop SEO endpoint:
SeoBundle — returned for products and collections. Contains everything a page needs:
SeoSiteBundle — returned by the site endpoint. Contains only { jsonLd }: the site-level Organization + WebSite (+ FAQPage) graph. It deliberately has no title/meta — your home page owns its own meta tags.
See the core API reference for the full type shapes.
The store API key
Every request is authenticated by your store API key (ask_…), sent as Authorization: Bearer. Generate and rotate it in the dashboard under Settings → API Keys.
- It’s a secret — server-side only, never in a client bundle or the browser. See Security.
- It resolves to exactly one store, so there’s no store id in the endpoint path. A merchant with several connected stores gets one key per store.
- Rotation is safe: after you rotate, the previous key keeps working for 24 hours so you can redeploy without downtime.
Caching
Because each response is scoped to a store by a secret key, the endpoint marks it private — it can’t be stored in a shared or CDN cache. Your framework’s own cache carries the load instead:
- Endpoint headers:
Cache-Control: private, max-age=3600, stale-while-revalidate=86400, plus an X-Cache: HIT|MISS header telling you whether the backend served the bundle from its own cache.
- Framework cache (the primary layer): the Next.js helpers wrap every fetch with
{ next: { revalidate: 3600 } } (ISR, 1 hour) by default, so a page’s bundle is fetched once per revalidation window, not per request.
Override the framework layer via fetchOptions:
Override the framework layer via fetchOptions:
Graceful degradation (the never-throw contract)
Every fetch helper returns null on any failure — missing configuration, a missing or invalid key (401), 404, timeout, network error. Components render nothing, metadata helpers return an empty object, and your page ships without SEO tags rather than crashing.
- A missing base URL logs a single
console.warn (once per process) and skips tags.
- A misconfigured store renders perfectly fine — pages return 200, just without SEO tags. The only exception is the sitemap route, which returns a 502 with an empty body when the bundle is unavailable (a sitemap must be real XML or an error).
- If you want loud failures instead — for example in a health check — call
resolveConfig(cfg), the throwing validation variant.
Where each artifact goes
| Artifact | Where | Helper |
|---|
| Product meta + JSON-LD | app/products/[handle]/page.tsx | createProductMetadata + <ProductJsonLd> |
| Collection meta + JSON-LD | app/collections/[handle]/page.tsx | createCollectionMetadata + <CollectionJsonLd> |
| Site-level JSON-LD | Home page only (app/page.tsx) | <SiteJsonLd> |
| Sitemap | app/sitemap.xml/route.ts | productSitemapRoute |
Inject <SiteJsonLd> exactly once, on the home page. Google discourages repeating the same FAQPage/site graph across many pages.