Attraction Product API

Integrate Attraction product data with REST/JSON requests. The current API covers promotional products, with public MSRP and account net prices through an API key.

Open OpenAPI JSON

Quick start

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

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

Catalog responses support gzip. The --compressed option requests a compressed response and lets cURL decode it automatically.

List products

Fetches 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 --compressed "https://api.attraction.com/v1/products?locale=en-CA"
Response excerpt
{
  "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, "price": 30.5 },
          { "minQuantity": 24, "maxQuantity": 99, "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

Fetches 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

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 excerpt
{
  "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",
  "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, "price": 30.5 },
      { "minQuantity": 24, "maxQuantity": 99, "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",
      "material": {
        "composition": "50% organic cotton, 50% recycled polyester",
        "weight": "7.4 oz/linear yd (150 gsm)",
        "gsm": 150
      },
      "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. regularPrice appears only when that price is reduced (a markdown) and names the original regular price. The same convention applies to pricing.msrp and pricing.net.

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

Net pricing through the API

With an account API key, the product detail response also returns the account's net prices, pricing.net.

The product detail response identifies the account's tier in pricing.priceTier; the pricing-only endpoints return priceTier at the top level. A key follows its account's price tier. Tier changes can take up to 60 seconds to appear in API responses.

The product list, /v1/products, stays MSRP-only either way. A whole catalog is priced through the bulk endpoint below.

Authenticated request

GET/v1/products/{sku}
curl -H "Authorization: Bearer $ATTRACTION_API_KEY" \
  "https://api.attraction.com/v1/products/100386U?locale=en-CA"
Response excerpt
{
  "sku": "100386U",
  "locale": "en-CA",
  "pricing": {
    "currency": "CAD",
    "priceTier": "A2",
    "msrp": [
      { "minQuantity": 1, "maxQuantity": 23, "price": 30.5 }
    ],
    "net": [
      { "minQuantity": 1, "maxQuantity": 23, "price": 22.75 }
    ],
    "sizes": [
      { "name": "XS" },
      { "name": "2XL", "surcharge": { "msrp": 4.5, "net": 3.4 } }
    ]
  }
}

Pricing for one product

GET /v1/products/{sku}/pricing returns the same price data as the product detail, without the rest of the product, shown in the response alongside. It has no public representation, so it always requires a key: a call with no credentials gets 401 key_required rather than an anonymous MSRP answer.

GET/v1/products/{sku}/pricing
curl -H "Authorization: Bearer $ATTRACTION_API_KEY" \
  "https://api.attraction.com/v1/products/100386U/pricing?locale=en-CA"
Response excerpt
{
  "sku": "100386U",
  "currency": "CAD",
  "priceTier": "A2",
  "msrp": [
    { "minQuantity": 1, "maxQuantity": 23, "price": 30.5 }
  ],
  "net": [
    { "minQuantity": 1, "maxQuantity": 23, "price": 22.75 }
  ],
  "sizes": [
    { "name": "XS" },
    { "name": "2XL", "surcharge": { "msrp": 4.5, "net": 3.4 } }
  ]
}

Pricing for the whole catalog

GET /v1/products/pricing returns that same price data for every product in the requested locale, in one response, with entries sorted by sku. Use this endpoint to update prices in a catalog already in storage. Like the single-product route, it always requires a key.

GET/v1/products/pricing
curl -H "Authorization: Bearer $ATTRACTION_API_KEY" \
  "https://api.attraction.com/v1/products/pricing?locale=en-CA"
Response excerpt
{
  "currency": "CAD",
  "priceTier": "A2",
  "products": [
    {
      "sku": "100222U",
      "msrp": [
        { "minQuantity": 1, "maxQuantity": 23, "price": 42 }
      ],
      "net": [
        { "minQuantity": 1, "maxQuantity": 23, "price": 31.5 }
      ],
      "sizes": [
        { "name": "2XL", "surcharge": { "msrp": 5, "net": 3.75 } }
      ]
    },
    {
      "sku": "100386U",
      "msrp": [
        { "minQuantity": 1, "maxQuantity": 23, "price": 30.5 }
      ],
      "net": [
        { "minQuantity": 1, "maxQuantity": 23, "price": 22.75 }
      ],
      "sizes": [
        { "name": "XS" },
        { "name": "2XL", "surcharge": { "msrp": 4.5, "net": 3.4 } }
      ]
    }
  ]
}

Authentication

Send your account API key as Authorization: Bearer attr_sk_... on each request that needs the account's net prices.

Keys are read-only and never expire; an account holds at most 10 active ones. On product detail and pricing routes, an unusable key gets 401 (invalid_key or key_revoked) rather than a silent fallback to MSRP.

Revoking a key can take up to 60 seconds to affect API responses while a cached verification expires.

Attraction Studio accounts are reserved for distributors. An account already linked to an Attraction distributor automatically receives Studio access, its price tier and the ability to create API keys. Other distributors need our team's approval before these become available.

Rate limits

Request quotas, response headers and retry timing are documented in the REST API rate-limit policy.

Versioning and deprecation

Check the REST API compatibility policy and the integration changelog for deprecation notices and migration requirements.

Error responses

Errors are RFC 9457 objects served as application/problem+json. The examples below show the response shape; each code links to its cause and recovery instructions. Public product requests work without credentials, but both pricing endpoints require an API key.

code is one of: route_not_found, method_not_allowed, invalid_request, unsupported_locale, invalid_sku, unauthorized, key_required, invalid_key, key_revoked, 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"
}

key_revoked

401 when the API key was revoked, or the Attraction account behind it no longer holds distributor pricing.

json
{
  "type": "https://www.attraction.com/developers/#error-key_revoked",
  "title": "API key revoked",
  "status": 401,
  "detail": "This API key is no longer active: it was revoked, or the Attraction account behind it no longer holds distributor pricing. Check the key and the account at https://www.attraction.com/api/keys/.",
  "instance": "/v1/products/100386u",
  "code": "key_revoked"
}

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