Attraction developer hub
Attraction publishes a read-only public Product API and Markdown mirrors of this site so people and AI agents can bring current product data and company context into their own tools. This page is the starting point; the full API reference covers every endpoint, parameter, and response field.
Start here
GET /v1/products?locale=en-US
curl "https://api.attraction.com/v1/products?locale=en-US"- Full endpoint reference: attraction.com/en-us/api/
- OpenAPI description: api.attraction.com/openapi.json
- No API key is required for public product data.
Authentication
Public product data needs no key and no signed-in account — product lists, detail, colors, sizes, live inventory, MSRP pricing, images, and decoration compatibility are all open reads.
Reading your account's net prices instead takes an API key, created on the API keys page and sent in an Authorization: Bearer header. It works on three routes:
GET /v1/products/{sku}— carriespricing.netbeside MSRP.GET /v1/products/{sku}/pricing— one product's prices alone.GET /v1/products/pricing— every product's prices in one response, for repricing a stored catalog.
The product list, /v1/products, stays MSRP-only either way. Neither pricing route has an anonymous fallback: calling one without credentials returns 401 key_required.
Creating a key takes an Attraction Studio account with distributor pricing on it. Anyone can sign up at studio.attraction.com: an account already tied to an Attraction distributor gets its price tier — and key creation — right away. A new distributor account is a request to our team instead; Studio access and key creation open once it's accepted.
A key reads the prices of the account that created it, and no other. Every authenticated response echoes pricing.priceTier, the tier those prices came from. A key follows the account's live tier, so if it changes, the key keeps working and simply returns the new prices.
Keys never expire and revoke instantly; an account holds at most 10 active keys. A malformed or unknown key gets 401 invalid_key; a revoked key, or one whose account no longer holds distributor pricing, gets 401 key_revoked. Neither falls back to MSRP: wrong prices are refused, not substituted.
GET /v1/products/{sku}/pricing
curl -H "Authorization: Bearer $ATTRACTION_API_KEY" \
"https://api.attraction.com/v1/products/100386U/pricing?locale=en-US"Rate limits
Every response from /v1/products and /v1/products/{sku} carries rate-limit headers for a policy named default: 600 requests per minute per client IP. A request carrying an API key is counted per key instead, under keyed: 1200 requests per minute. Both counts are advisory, tracked per API instance rather than one global counter — not a sizing guarantee for an integration. Infrastructure also enforces a hard backstop of 100 requests per second (burst 200) regardless. These headers follow draft-ietf-httpapi-ratelimit-headers; a request over the limit gets 429 with Retry-After and a rate_limited error, which must always be honored.
RateLimit-Policy: "default";q=600;w=60RateLimit-Policy: "keyed";q=1200;w=60(requests authenticated with an API key)RateLimit: "default";r=<remaining>;t=<seconds>
Catalog routes also serve gzip: when Accept-Encoding: gzip is sent, the roughly 274 KB product list compresses to about 24 KB.
Versioning and deprecation
The API is versioned in the URL: today's endpoints are under /v1. Breaking changes ship as /v2 rather than changing /v1 in place; additive changes, such as a new field or a new optional parameter, never bump the version.
When a version is deprecated, we announce it at least three months ahead and mark every response with a Deprecation header (RFC 9745) and a Sunset header (RFC 8594) naming the retirement date.
Errors
Errors are RFC 9457 problem+json objects, served as application/problem+json:
product_not_found — 404
{
"type": "https://www.attraction.com/developers/#error-product_not_found",
"title": "Product not found",
"status": 404,
"detail": "No product matches SKU NOPE-NOT-A-SKU.",
"instance": "/v1/products/nope-not-a-sku",
"code": "product_not_found"
}Each code below has its own type URI on this page: https://www.attraction.com/developers/#error-<code>.
route_not_found — 404
No route matches the request path.
method_not_allowed — 405
The route exists but not for that HTTP method. The response carries Allow: GET, HEAD, OPTIONS.
invalid_request — 400
The request does not match what the route expects, for example a malformed query parameter.
unsupported_locale — 400
locale must be en-CA, en-US, or fr-CA.
invalid_sku — 400
The sku path parameter is not a valid SKU.
unauthorized — 401
Reserved for the private /v1/sync/* routes; the public product endpoints never return this.
key_required — 401
GET /v1/products/{sku}/pricing was called with no credentials. That route prices one account, so it has no public fallback — unlike the product endpoints, which answer anonymously with MSRP. The response carries WWW-Authenticate: Bearer realm="api.attraction.com" with no error parameter: nothing was presented to reject.
invalid_key — 401
The Authorization: Bearer header carries a key the API cannot recognise. detail says which: not recognised (often copied incompletely), or malformed, meaning it lacks the shape attr_sk_<16 hex characters>_<43 characters>. Either way, create a new one on the API keys page. The response carries WWW-Authenticate: Bearer realm="api.attraction.com", error="invalid_token". A request with no credentials at all is not an error. It simply reads public data.
key_revoked — 401
The API key is no longer active: it was revoked, or the account behind it no longer holds distributor pricing (closed, deactivated, or off its price tier). The key and account can be checked on the API keys page. A tier change never causes this — a key follows its account's live tier.
product_not_found — 404
No product matches the given SKU.
rate_limited — 429
The client is over the rate-limit policy. The response carries Retry-After and the RateLimit/RateLimit-Policy headers.
upstream_unavailable — 503
The catalog source is temporarily unreachable. The response carries Retry-After: 30.
internal_error — 500
An unexpected server error, with no stack trace or internal detail in the response.
Agent resources
- Sending
Accept: text/markdownto any page URL, or appending.md(for example attraction.com/products.md) returns a Markdown copy of that page. - llms.txt and llms-full.txt index the site for language models and agents.
- .well-known/api-catalog lists the API's machine-readable entry points (RFC 9727).
- .well-known/agent-skills/index.json publishes Agent Skills for the Product API and the site's Markdown pages.
Status and support
/v1/health reports API status. For anything else, reach us at customerservice@attraction.com.
Changelog
- 2026-08-31 — Net pricing through the API: an account API key, created and revoked on the API keys page, adds your account's net prices to
/v1/products/{sku}and to two new pricing-only endpoints —/v1/products/{sku}/pricing(one product) and/v1/products/pricing(the whole catalog). Keys are bearer tokens that follow the account's live price tier, and bring theinvalid_keyandkey_revokederror codes plus thekeyedrate-limit policy. - 2026-08-31 — Per-color materials: product detail colors now carry
material(thecomposition, a locale-formattedweightlabel, the numericgsm, and, on Jameo products, the fabricname). The former top-levelmaterialsarray and the per-colorweightstring are removed. - 2026-08-25 — Product freshness: every product now reports
updatedAt, set only when its product content actually changes (inventory movements never affect it), and the product list reportscatalogUpdatedAtwhen a product is added or removed. - 2026-08-24 — Published this developer hub. Error responses moved to RFC 9457
application/problem+json; added rate-limit headers and a versioning and deprecation policy. - 2026-06-26 — Official launch: the read-only public Product API at api.attraction.com (product list and detail under
/v1, no key required) with its OpenAPI description and interactive documentation.