Skip to main content
The SDK is a thin client over a handful of API-key-authenticated endpoints. You can call them directly from any server-side stack. Base URL: https://api.useagentshop.com/api/v1/headless-seo The crawler endpoint is a sibling of that base — https://api.useagentshop.com/api/v1/crawler-hits.

Authentication

Every request must carry your store’s API key. The key resolves to exactly one store, so there’s no store id or token in the path.
The backend also accepts the key in an x-api-key: ask_xxxxxxxx… header if you prefer. The key is a secret — only call these endpoints from a server (never a browser). Get and rotate it in the dashboard under Settings → API Keys. See Security.

Endpoints

GET {base}/product/{handle}

Returns the product SeoBundle for a product handle (URL-encoded).

GET {base}/collection/{handle}

Returns the collection SeoBundle. Same shape as a product bundle; openGraph.type is "website".

GET {base}/site

Returns the SeoSiteBundle{ jsonLd } only, the site-level Organization + WebSite (+ FAQPage) graph.

GET {base}/sitemap.xml

Returns the product sitemap as XML (application/xml).

GET {base}/pixel

Returns where to load the storefront analytics script from, and what the analytics destination needs. See Storefront analytics.
string
required
Fully-formed URL of the pixel script, token included. All <AgentShopPixel /> needs.
string
required
The store’s pixel token. Browser-visible by design — it already rides in scriptUrl, and pixel ingestion is a public endpoint.
string
required
Where the analytics destination posts events.
string
required
The store’s Shopify domain, as ingestion expects to receive it.
Cache-Control: no-store: the response is a per-store credential on a URL that is identical for every store — only the Authorization header differs — so no HTTP cache may ever replay it across keys. Freshness comes from the SDK re-resolving per render (the Next helper caches for 5 minutes in your app, not in HTTP). Returns 404 when the store has no pixel configured.
eventsUrl is not one of the /api/v1 routes and does not take a Bearer token. It authenticates on the pixel_token in the request body, and the SDK sends no Authorization header at all — it posts Content-Type: text/plain;charset=UTF-8 so the beacon stays preflight-free. Adding a Bearer header to a custom sender breaks it twice over: the header is ignored, and it forces a CORS preflight the beacon can’t make.
The analytics destination and the served script both POST events to eventsUrl. Every event is accepted and captured whether or not it carries an AI acquisition signal — the response is {"success": true, "attributed": <boolean>}, true when the server resolved the event to an AI source (sent by the served script, or inferred server-side from the raw signals). Unattributed events don’t surface as AI-referred visits — see why you may see no events. The one drop is bot and search-crawler user agents, answered with {"success": true, "skipped": true}. event_data is an allowlisted flat projection (product_id, product_title, price, quantity, variant_id, handle, collection_id, search_term — whichever apply to the event); the raw Shopify bus payload is deliberately not forwarded. See Storefront analytics for the full picture.

POST /api/v1/crawler-hits

Records one AI-crawler request to your storefront. See AI crawler visibility. An accepted hit returns 202, with { "recorded": true } when it was handed to the analytics pipeline or { "recorded": false } if that failed after acceptance — in both cases there is nothing to retry. 202 is not the only outcome. The request body is validated strictly, so any of these return 400: a url or referrer over 2048 characters or on a non-http(s) scheme, method over 16, statusCode outside 100–599, a blank or over-long userAgent, or any field not listed above. A bad or missing key returns 401, and exceeding the crawler-hits budget returns 429. If you wire “202 means done”, long product URLs disappear with no signal — check the status, not just the body.
string
required
Absolute URL of the request. Must be http: or https: — other schemes are rejected.
string
required
HTTP method.
string
required
The User-Agent header, verbatim. Identification happens server-side.
number
Response status, when your runtime knows it. Omit rather than guess — see Status codes.
string
The Referer header, if present. Must be http: or https: if sent — other schemes are rejected, not stripped, so omit rather than guess if you’re unsure.
No IP address is accepted.

Response shape (SeoBundle)

object
required
The schema.org JSON-LD graph, e.g. { "@context": "https://schema.org", "@graph": [Product, BreadcrumbList] }. Serialize with serializeJsonLd before embedding — see Security.
string
required
The page title, from your Shopify SEO fields.
string
required
The meta description.
string
required
The canonical URL for the page.
object
required
{ title, description, type: "product" | "website", url, siteName?, image? }
object
required
{ card: "summary" | "summary_large_image", title, description, image? }

Caching

All responses are served with:
The cache is private — because each response is scoped to a store by a secret key, it must not be stored in a shared or CDN cache. Two responses differ: /pixel sends no-store (its body is a credential), and a stale-but-served sitemap sends private, no-cache. Instead, your framework’s own cache does the work (the Next.js helpers add revalidate: 3600 ISR by default). X-Cache tells you whether the backend served the bundle from its cache.

Rate limits

Rate limits are per key (i.e. per store), in separate pools: Crawler reporting has its own, larger budget on purpose: a multi-bot recrawl after a sitemap update is a burst, and it must not starve the same store’s SEO-artifact serving — or be starved by it. Over the limit returns 429. Standard RateLimit-* headers are on every response.

Status codes

The SDK treats any non-2xx as a null bundle (see the never-throw contract); if you call the endpoints directly, handle non-200 the same way — render the page without SEO tags (except the sitemap route, which should surface the error).