Attraction Product API

Bring public Attraction product data into your catalog, product search, or assistant workflow.

Open OpenAPI JSON

Quick start

Run this request from a terminal to fetch public product summaries.

bash
curl "https://api.attraction.com/v1/products?locale=en-CA"

List products

Fetch product summaries for catalogs, listing pages, search, filters, and color exploration.

Query parameter

ParameterRequiredValuesDefault
localeNoen-CA, en-US, fr-CAen-CA

Public request

GET/v1/products
curl "https://api.attraction.com/v1/products?locale=en-CA"
Response
{
  "locale": "en-CA",
  "products": [
    {
      "sku": "100386U",
      "slug": "100386u",
      "productName": "Crewneck T-shirt",
      "descriptions": {
        "tagline": "Crewneck t-shirt, our best-selling t-shirt. Ethica made in Canada.",
        "short": "Crewneck t-shirt in jersey in organic cotton and recycled polyester, or 100% organic cotton depending on the colour, our best-selling t-shirt, wide colour range. Ethica made in Canada."
      },
      "brand": "Ethica",
      "customization": {
        "colorBlocks": false
      },
      "catalog": {
        "inspiration": ["Promotional"],
        "category": "T-shirts",
        "for": "Unisex"
      },
      "keywords": ["t shirt","custom t shirt","crewneck","ethica","made in canada"],
      "tag": "Best seller",
      "minimumOrder": {
        "quantity": 36,
        "description": "Minimum 36 decorated units. No minimum undecorated."
      },
      "studio": {
        "available": true
      },
      "pricing": {
        "currency": "CAD",
        "msrp": [
          { "minQuantity": 1, "maxQuantity": 23, "regularPrice": 30.5, "price": 30.5 },
          { "minQuantity": 24, "maxQuantity": 99, "regularPrice": 28, "price": 28 },
          ...
        ]
      },
      "images": [
        {
          "url": "https://www.attraction.com/assets/products/100386u/100386u-primary.webp",
          "alt": "White Ethica crewneck T-shirt"
        },
        ...
      ],
      "colors": [
        {
          "id": "0001",
          "pms": "White",
          "name": "White",
          "swatches": ["#FFFFFF"],
          "colorFamilies": [
            {
              "name": "White",
              "hex": "#F8F8F2"
            }
          ],
          "images": [
            "https://www.attraction.com/assets/products/100386u/100386u-white.webp"
          ]
        },
        ...
      ],
      ...
    },
    ...
  ],
  "categories": [
    {
      "name": "T-shirts",
      "order": 10
    },
    ...
  ],
  "brands": {
    "Ethica": {
      "name": "Ethica",
      "url": "https://www.attraction.com/en-ca/products/ethica/",
      "description": "Timeless classics, made in Canada with sustainable fibres."
    },
    ...
  }
}

Get product details

Fetch one product for a product page, catalog sync, inventory check, asset lookup, or decoration compatibility lookup.

Path parameter

ParameterDescription
skuProduct SKU, for example 100386U.

Query parameter

ParameterRequiredValuesDefault
localeNoen-CA, en-US, fr-CAen-CA

Public request

Fetch product detail, MSRP pricing, available sizes and size upcharges, inventory, color metadata, materials, media, resource links, and compatible decoration methods without authentication.

GET/v1/products/{sku}
curl "https://api.attraction.com/v1/products/100386U?locale=en-CA"
Response
{
  "sku": "100386U",
  "slug": "100386u",
  "locale": "en-CA",
  "productName": "Crewneck T-shirt",
  "descriptions": {
    "tagline": "Crewneck t-shirt, our best-selling t-shirt. Ethica made in Canada.",
    "short": "Crewneck t-shirt in jersey in organic cotton and recycled polyester, or 100% organic cotton depending on the colour, our best-selling t-shirt, wide colour range. Ethica made in Canada.",
    "long": "Crewneck t-shirt. Fabric: jersey in organic cotton and recycled polyester, or 100% organic cotton depending on the colour. Key features: our best-selling t-shirt, wide colour range."
  },
  "keywords": ["t shirt","custom t shirt","crewneck","ethica","made in canada"],
  "note": "For women, order a smaller size than usual (M=S), since this is a unisex style.",
  "brand": {
    "name": "Ethica",
    "url": "https://www.attraction.com/en-ca/products/ethica/",
    "description": "Timeless classics, made in Canada with sustainable fibres."
  },
  "insufficientInventoryMessage": "Insufficient inventory for your project? Ethica styles can usually be produced quickly. Contact us and we'll confirm lead times.",
  "catalog": {
    "inspiration": ["Promotional"],
    "category": "T-shirts",
    "for": "Unisex"
  },
  "links": {
    "studio": "https://studio.jameo.com/home?lang=en"
  },
  "tag": "Best seller",
  "materials": [
    {
      "code": "JERSEY",
      "label": "Jersey"
    }
  ],
  "minimumOrder": {
    "quantity": 36,
    "description": "Minimum 36 decorated units. No minimum undecorated."
  },
  "customization": {
    "colorBlocks": false,
    "decorations": [
      {
        "name": "Digital Print (DTG)",
        "note": "",
        "icon": "https://www.attraction.com/assets/icons/digital-printing.svg",
        "link": "https://www.attraction.com/en-ca/decoration/#digital-printing"
      }
    ]
  },
  "studio": {
    "available": true
  },
  "pricing": {
    "currency": "CAD",
    "msrp": [
      { "minQuantity": 1, "maxQuantity": 23, "regularPrice": 30.5, "price": 30.5 },
      { "minQuantity": 24, "maxQuantity": 99, "regularPrice": 28, "price": 28 },
      ...
    ],
    "sizes": [
      { "name": "XS" },
      { "name": "2XL", "surcharge": { "msrp": 4.5 } },
      ...
    ]
  },
  "images": [
    {
      "url": "https://www.attraction.com/assets/products/100386u/100386u-primary.webp",
      "alt": "White Ethica crewneck T-shirt"
    },
    ...
  ],
  "assets": {
    "images": [
      {
        "url": "https://www.attraction.com/assets/products/100386u/100386u-primary.webp",
        "alt": "White Ethica crewneck T-shirt"
      },
      ...
    ],
    "resources": [
      {
        "type": "sizeChart",
        "label": "Size chart",
        "url": "https://www.attraction.com/assets/products/100386u/100386u.pdf"
      },
      ...
    ]
  },
  "colors": [
    {
      "id": "0001",
      "pms": "White",
      "name": "White",
      "weight": "7.4 oz/linear yd (150 gsm)",
      "swatches": ["#FFFFFF"],
      "colorFamilies": [
        {
          "name": "White",
          "hex": "#F8F8F2"
        }
      ],
      "images": [
        "https://www.attraction.com/assets/products/100386u/100386u-white.webp"
      ]
    },
    ...
  ],
  "inventory": [
    { "colorId": "0001", "size": "XS", "quantity": 124 },
    ...
  ]
}

Discounted prices

In each tier, price is the current price and regularPrice is the regular price before the discount; if price < regularPrice, the tier is discounted, otherwise it is not. The same convention applies to pricing.msrp and pricing.net.

Response
{
  "pricing": {
    "msrp": [
      { "minQuantity": 1, "maxQuantity": 23, "regularPrice": 30, "price": 25 }
    ],
    "net": [
      { "minQuantity": 1, "maxQuantity": 23, "regularPrice": 18, "price": 15 }
    ]
  }
}

Net pricing through the API

Public endpoints return product content, MSRP pricing, available sizes, size-specific MSRP upcharges, inventory, color metadata, media, resources, and product-detail decorations. Bearer-token/API authentication for account-specific net pricing is not available yet.

Authentication

Public product data needs no key and no signed-in account: product lists, product detail, colors, sizes, live inventory, MSRP pricing, images, and decoration compatibility are all open reads. Account API keys for distributor net pricing are not available yet; the API keys page stays hidden until that ships.

Rate limits

Every response under /v1/products carries rate-limit headers for a policy named "default": 600 requests per minute per client IP. This count is advisory and tracked per API instance, not a single global counter, so do not size an integration against 600/min as a guarantee. Infrastructure also enforces a hard backstop of 100 requests per second (burst 200) regardless of the advisory count. RateLimit-Policy: "default";q=600;w=60 and RateLimit: "default";r=<remaining>;t=<seconds> follow draft-ietf-httpapi-ratelimit-headers. Requests over the limit get a 429 with Retry-After — always honor it. Catalog routes also serve gzip: send Accept-Encoding: gzip and 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. Deprecations are announced at least three months ahead, with Deprecation (RFC 9745) and Sunset (RFC 8594) headers naming the retirement date.

Error responses

Errors are RFC 9457 problem+json objects, served as application/problem+json, with type, title, status, detail, instance, and code fields. Missing authentication does not error because product data is public. Each code has its own type URI on the full error reference at /developers/#errors.

code is one of: route_not_found, method_not_allowed, invalid_request, unsupported_locale, invalid_sku, unauthorized, product_not_found, rate_limited, upstream_unavailable, internal_error.

unsupported_locale

400 when locale is not one of en-CA, en-US, or fr-CA.

json
{
  "type": "https://www.attraction.com/developers/#error-unsupported_locale",
  "title": "Unsupported locale",
  "status": 400,
  "detail": "Supported locales are en-CA, en-US, and fr-CA.",
  "instance": "/v1/products",
  "code": "unsupported_locale"
}

product_not_found

404 for /v1/products/{sku} when the SKU does not exist.

json
{
  "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"
}

rate_limited

429 when the request exceeds the v1 rate limit.

json
{
  "type": "https://www.attraction.com/developers/#error-rate_limited",
  "title": "Too many requests",
  "status": 429,
  "detail": "The default rate limit is 600 requests per minute per client IP. Retry after the window resets.",
  "instance": "/v1/products",
  "code": "rate_limited"
}