Shop the look

post/v2/agent/match

Finds the products in a photo of someone dressed. The shop-the-look models read the photo once, find every item the person wears (garments, shoes, bags, jewellery, eyewear, hats), and match each one against your store's products. For every item you get the best product and the other high-scoring ones, best first, and whether the top one is the exact product (match: exact) or the closest your store sells (similar).

Experimental. Shop the look is new. Its request and response fields may change, and its matches are still improving.

A run takes about a second. No language model is involved: the models are trained for this and run on our servers.

The photo

Send it inline as a data: URI (PNG, JPEG or WebP, at most 8,000,000 characters), or as imageUrl, a public http(s) URL we fetch (a domain name, not an IP address; redirects are not followed). Photos of a person wearing the outfit work best; about 1024px on the long side is plenty.

Which store

One store is searched. With itemId, the photo is that product's own (a product page's model shot): its store is searched, the product is read with its title, and it is never answered for itself — nor are its other colourways, or the other pieces of a set it belongs to. This is how the complete-the-look strip reads a product page. Without itemId, datasetIds (default: every active dataset) is searched, narrowed to one storefront by store.

A store's products are searchable once they are indexed, which happens after each sync; indexed: false means none is yet.

Prices

Send country and every card is priced in that shopper's market. A product the market does not sell gives way to the next alternate that it does.

Public keyapi.v2.agent.matchDemo key OK

Request body

application/jsonrequired
imagestring

The photo as a data: URI — data:image/png;base64,…, data:image/jpeg;base64,… or data:image/webp;base64,…. Longer than 8,000,000 characters is refused with 413.

maxLength: 8000000e.g. "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
imageUrlstring (uri)

Or a public http(s) URL of the photo, fetched by us: a domain name, never an IP address or an internal host, with no redirects, at most 12 MB. Used when image is absent.

e.g. "https://cdn.shop.example.com/files/moto-jacket-look.jpg"
itemIdstringnullable

The product the photo belongs to, when it is a product's own photo. Its store is searched, it is read with its title, and it, its colourways and its set's pieces are never offered. A 404 when it is not a product in the resolved datasets.

default: nulle.g. "8f2c1a9e-4b7d-4c1e-9a3f-2d6b7e5c1a90"
datasetIdsarray<string>

Search these datasets. Omit or send [] to use every active dataset in the organization. Ignored for scope when itemId is sent (the product's store is searched), but still checked.

default: []
storestringnullable

A storefront URL or domain, narrowing the datasets to that store's. A 404 when no dataset in scope is that store.

default: nulle.g. "shop.example.com"
countrystringnullable

The shopper's country, ISO 3166-1 alpha-2 in any case (US, ca); on a Shopify storefront, Shopify.country. Cards are priced in that shopper's market, and a product the market does not sell is replaced by its next alternate that it does. Omitted, or for a country the store does not sell into, cards carry the store's default market's prices, flagged marketExact: false.

default: nulle.g. "US"
alternatesinteger

How many products to return per item besides the top one.

default: 4min: 0max: 4e.g. 2

Response

200The items found, each with its products.

Response headers

X-RateLimit-Resource

The quota resource this endpoint is metered against: api.v2.agent.chat for the chat endpoint, api.v2.agent.looks for the looks endpoint and api.v2.agent.match for the shop-the-look endpoint. These are separate from the v1 resources, so Agent API usage never draws down a v1 allowance.

X-RateLimit-Quota-Limit

Requests allowed in the current billing period, or unlimited.

X-RateLimit-Quota-Remaining

Requests left in the current billing period, or unlimited.

X-RateLimit-Quota-Reset

Unix epoch seconds at which the billing period resets.

X-RateLimit-RPM-Limit

Requests allowed per minute for this resource.

X-RateLimit-RPM-Remaining

Requests left in the current one-minute window.

X-RateLimit-RPM-Reset

Unix epoch seconds at which the one-minute window resets.

array<MatchGarment>required

The items found, in the order the model is most sure of them. Empty when nothing the store sells is in the photo.

slotstringrequired

What it is, in a shopper's word — the top product's family (jacket, sneaker, tote bag), or the family read from the photo when the product has none.

e.g. "jacket"
familystringrequired

What the model read in the photo, as slot/family in the product taxonomy.

e.g. "outerwear/jacket"
colourstringnullablerequired

The colour the model read, one of the taxonomy's colour families.

e.g. "black"
boxarray<number>required

Where the item is in the photo, [x0, y0, x1, y1] as fractions of its width and height.

minItems: 4maxItems: 4
matchstringrequired

exact — the model is sure item is the product in the photo (on stores it never trained on, 98.6% of its exact calls are right). similar — item is the closest the store sells: a look-alike, or the exact product when the model is not sure enough to say so.

One ofexactsimilar
e.g. "exact"
MatchCardrequired

A catalogue item as the match endpoint presents it — enough to draw a card and link out, without the full Product record.

array<MatchCard>required

The next highest-scoring products, best first, one per product (colourways of one product are not repeated). As many as alternates asked for, or fewer.

maxItems: 4
indexedbooleanrequired

false when none of the store's products is indexed yet (a store connected minutes ago); try again after its first sync has been processed.

e.g. true

Errors

32

Every error is JSON with a single error string unless noted. Match on the status code; the message is for humans.

400
Request body must be valid JSON.

The body could not be parsed as JSON.

400
Request body must be a JSON object.

The body is empty, or is valid JSON that is not an object (for example an array).

404
No active datasets found for this organization. Activate one via the dashboard (Dataset → Settings) or through the REST API before proceeding.

datasetIds was omitted and the organization has datasets, but none is active.

404
All requested datasets are archived. Reactivate at least one via the dashboard or REST API.

Every dataset in datasetIds is archived.

404
No datasets found for this organization. Create one via the dashboard or REST API, or pass datasetIds to use draft/archived datasets before proceeding.

The organization has no datasets at all.

404
itemId is not a product in these datasets.

itemId is not a product of this organization in the resolved datasets.

404
store matches none of these datasets.

store is not the storefront (storeDomain) of any dataset in scope.

413
image is too large.

image is longer than 8,000,000 characters, or the photo at imageUrl is over 12 MB.

422
Send the photo as image (a data URL) or imageUrl.

Neither image nor imageUrl was sent.

422
image must be a base64 data URL (png, jpeg or webp).

image is not a string starting with data:image/png;base64,, data:image/jpeg;base64, (or jpg) or data:image/webp;base64,.

422
imageUrl: <reason>

imageUrl is not an http(s) URL on a public domain name (an IP address, localhost or an internal name is refused).

422
imageUrl could not be fetched.

The photo at imageUrl could not be fetched (a network error, a timeout, a redirect, or an error status, which is quoted), or is not an image.

422
alternates must be an integer from 0 to 4.

alternates is present and not an integer from 0 to 4.

422
datasetIds must be an array of strings.

datasetIds is present and is not an array, or contains a value that is not a string.

422
Invalid datasetIds for this organization: <ids>

One or more of datasetIds does not belong to the organization. The offending ids are listed, comma-separated.

curl -X POST "https://stylor.ai/api/v2/agent/match" \
  -H "Authorization: Bearer $STYLOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...",
  "store": "shop.example.com",
  "country": "US"
}'
Response · 200
{
  "indexed": true,
  "garments": [
    {
      "slot": "jacket",
      "family": "outerwear/jacket",
      "colour": "black",
      "box": [
        0.18,
        0.21,
        0.83,
        0.58
      ],
      "match": "exact",
      "item": {
        "id": "8f2c1a9e-4b7d",
        "itemId": "8f2c1a9e-4b7d-4c1e-9a3f-2d6b7e5c1a90",
        "name": "Moto Leather Jacket",
        "brand": "Acme",
        "price": 340,
        "currency": "USD",
        "url": "https://shop.example.com/products/moto-leather-jacket",
        "image": "https://stylor.ai/api/v1/image/org_5f3a/ds_9c21/8f2c1a9e-4b7d/0.jpg/-/resize/600x/-/format/jpeg",
        "slot": "outerwear",
        "family": "jacket",
        "colour": "black"
      },
      "alternates": [
        {
          "id": "1b7e33c0-9a2d",
          "itemId": "1b7e33c0-9a2d-4f8e-b5c6-7d8e9f0a1b2c",
          "name": "Biker Jacket",
          "brand": "Acme",
          "price": 290,
          "currency": "USD",
          "url": "https://shop.example.com/products/biker-jacket",
          "image": "https://stylor.ai/api/v1/image/org_5f3a/ds_9c21/1b7e33c0-9a2d/0.jpg/-/resize/600x/-/format/jpeg",
          "slot": "outerwear",
          "family": "jacket",
          "colour": "black"
        }
      ]
    }
  ]
}