StyleMe from ZukMe Marketplace API
Integrate ZukMe's marketplace into your store or app, and dress every visiting customer in a complete, best-fit outfit made from real, purchasable pieces, on its own or alongside your own inventory, the kind of fit that turns window-shoppers into buyers. Matching only your own products? See StyleMe for Your Own Inventory.
Quickstart
Photo
of your user
StyleMe
style reading
1 creditMarketplace Try-On
rendered look
1 credit- 1Create an API key in the API Dashboard, then contact our team to get credits added — a new key starts with none.
- 2Submit a photo to
POST /api/v1/stylemefor a style reading. - 3Submit that reading and a photo to
POST /api/v1/marketplace/tryon. - 4Poll with the returned
jobIdevery 2–3s until done.
User experience flow
A plain walkthrough of what your shopper experiences on your own site or app — no implementation detail, just the moments. You build the interface; each beat notes which call powers it.
Uploads or picks a photo
One clear photo of themselves — the only input this flow needs.
→ POST /api/v1/styleme
A short wait while their style is read
One photo in; a few seconds later, a personalized style profile out.
→ POST /api/v1/styleme (poll)
Sees their style read back to them (optional)
A style title, a palette, a few personality notes — however much of the reading you choose to surface before moving on.
Picks an occasion (optional)
"Weekend brunch," "Job interview," whatever your UI offers — shapes which real pieces get shopped for next.
A short wait while an outfit is matched and rendered
Real, in-stock pieces from ZukMe's marketplace are matched to the reading and rendered onto their photo.
→ POST /api/v1/marketplace/tryon
Sees themselves in the finished look
The rendered photo, plus every piece used broken out individually — name, shop, price.
Taps a piece to buy it
Every item in the outfit is real and purchasable — you decide whether that's an affiliate link out or a straight add-to-cart on your own storefront.
Base URL
https://zukme.comAll endpoints below are relative to this base URL.
Authentication
Every request needs your API key as a bearer token — create and manage keys in the API Dashboard. The raw key is shown once; only its hash is stored.
Authorization: Bearer zk_live_<your key>Missing, malformed, or revoked key → 401:
{ "error": "Invalid or missing API key. Pass it as \"Authorization: Bearer zk_live_...\"." }Rate limits
30 requests per minute per key (submit and poll both count). Over the limit → 429:
{ "error": "Rate limit exceeded.", "retryAfterSeconds": 12 }Credits
Each successful submission costs 1 credit, deducted at submit and refunded automatically if generation fails. Polling is always free. A new API key starts with 0 credits — API and embed-widget usage spends from your account's bonus credit balance only, never your app subscription's monthly quota. Contact our team to get bonus credits added before building against this in production. Check your balance in the API Dashboard.
Errors
| Field | Type | Description |
|---|---|---|
400 | Bad Request | Missing or invalid fields in the request body. |
401 | Unauthorized | Missing, malformed, or revoked API key. |
402 | Payment Required | Credits exhausted (code CREDITS_EXHAUSTED), with your current looks/bonus/total balance in the body. |
404 | Not Found | jobId does not exist (expired, wrong id, or never submitted). |
429 | Too Many Requests | Rate limit exceeded for this key. See retryAfterSeconds. |
Example 402 body:
{
"error": "CREDITS_EXHAUSTED",
"code": "CREDITS_EXHAUSTED",
"looks": 0,
"bonus": 0,
"total": 0
}Step 1 — StyleMe reading
Input: one photo. Output: a personalized style profile (see the reading object below) that feeds directly into Marketplace Try-On. Async: submit, then poll.
/api/v1/stylemeSubmit| Field | Type | Description |
|---|---|---|
photoBase64required | string | Base64-encoded photo of the person to generate a reading for. |
Response (submitted):
{ "ok": true, "jobId": "a91c...", "status": "processing" }/api/v1/stylemePollResponse when done:
{ "ok": true, "status": "done", "reading": { /* StyleMeReading, see below */ } }The reading object
| Field | Type | Description |
|---|---|---|
valid | boolean | False if the photo could not be read (see rejection_reason). |
rejection_reason | string | null | Explanation when valid is false. |
styleTitle | string | The person's style archetype, e.g. "The Modern Minimalist". |
styleSubtitle | string | One-line elaboration on the style title. |
reading | string[] | Paragraphs of the full style reading. |
personality | { trait, level, description }[] | Personality trait breakdown, level 0–100. |
palette | { hex, name }[] | Colours that suit this person's colouring. |
outfits | { name, occasion, pieces, why }[] | Suggested outfit concepts. |
tags | string[] | Short style keyword tags. |
gender | string | undefined | Inferred gender, for downstream use. |
bodyType | string | undefined | Inferred body type, e.g. "M", "Petite". |
Step 2 — Marketplace Try-On
Matches the best real product per category from ZukMe's marketplace and renders them onto the photo. Async: submit, then poll.
/api/v1/marketplace/tryonSubmit| Field | Type | Description |
|---|---|---|
readingrequired | StyleMeReading | The reading object returned by POST /api/v1/styleme. |
photoBase64required | string | Base64-encoded photo of the person to render the outfit onto. |
occasion | string | Optional occasion (e.g. "Weekend brunch") that shapes which pieces are shopped for. |
bodyType | string | Optional body type override, e.g. "M", "Petite". Defaults to the reading's own inferred bodyType. |
callbackUrl | string | Optional webhook. See Callbacks below. |
Response (submitted):
{ "ok": true, "jobId": "c7e2...", "status": "processing" }/api/v1/marketplace/tryonPollResponse when done:
{
"ok": true,
"status": "done",
"imageUrl": "https://...",
"outfitBreakdown": [
{
"category": "top",
"type": "matched",
"productId": "abc123",
"productName": "Silk Wrap Blouse",
"shopName": "Studio Nine",
"imageUrl": "https://...",
"price": "USD 84",
"source": "zukme"
},
{
"category": "shoes",
"type": "invented",
"reason": "No good match found in the marketplace for this piece.",
"brief": "Sleek black ankle boots with a low block heel.",
"bestMatch": {
"productId": "cj-9182",
"productName": "Leather Block-Heel Boot",
"shopName": "Studio Nine",
"imageUrl": "https://...",
"price": "USD 96",
"clickUrl": "https://...",
"explanation": "Closest real match for the designed silhouette."
}
}
]
}Every entry has a type."matched" is a real, purchasable product — source is "zukme" or "cj", worn exactly as photographed. "invented" means nothing in the marketplace was a good enough fit for that category — the piece worn in the photo was designed by the AI instead of left empty, and (when available) bestMatch is a real product that resembles it, offered purely as a "shop similar" suggestion — it is never the piece actually rendered on the photo.
Callbacks (webhooks)
runway-edit and marketplace/tryon accept an optional callbackUrl on submit. When the job finishes (success or failure), we POST the same payload a completed poll would return — in addition to, never instead of, polling. Best-effort delivery with no retries, so keep polling as your source of truth.
// what we POST to callbackUrl on success:
{ "jobId": "...", "status": "done", "imageUrl": "https://...", "outfitBreakdown": [...] }
// on failure:
{ "jobId": "...", "status": "error", "error": "..." }callbackUrl must be a public http(s) URL — localhost and private network addresses are rejected.
Full example
Poll every 2–3s until status is "done" or "error". A 404 means the job expired or never existed — treat it as failed.
# 1. Get a StyleMe reading first (Step 2 above)
# 2. Submit
curl https://zukme.com/api/v1/marketplace/tryon \
-X POST \
-H "Authorization: Bearer zk_live_..." \
-H "Content-Type: application/json" \
-d '{
"reading": { /* from POST /api/v1/styleme */ },
"photoBase64": "<base64>",
"occasion": "Weekend brunch"
}'
# → { "ok": true, "jobId": "c7e2...", "status": "processing" }
# 3. Poll until done
curl https://zukme.com/api/v1/marketplace/tryon \
-X POST \
-H "Authorization: Bearer zk_live_..." \
-H "Content-Type: application/json" \
-d '{ "action": "poll", "jobId": "c7e2..." }'
# → { "status": "processing", "stage": "edit" } (repeat)
# → { "ok": true, "status": "done", "imageUrl": "https://...", "outfitBreakdown": [...] }const KEY = process.env.ZUKME_API_KEY;
const BASE = "https://zukme.com/api/v1/marketplace/tryon";
async function call(body) {
const res = await fetch(BASE, {
method: "POST",
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
return res.json();
}
const submitted = await call({
reading, // from POST /api/v1/styleme
photoBase64,
occasion: "Weekend brunch",
});
let result;
while (true) {
result = await call({ action: "poll", jobId: submitted.jobId });
if (result.status === "done" || result.status === "error") break;
await new Promise((r) => setTimeout(r, 2500));
}
console.log(result); // { ok: true, status: "done", imageUrl: "...", outfitBreakdown: [...] }import os, time, requests
KEY = os.environ["ZUKME_API_KEY"]
BASE = "https://zukme.com/api/v1/marketplace/tryon"
headers = {"Authorization": f"Bearer {KEY}"}
submitted = requests.post(BASE, headers=headers, json={
"reading": reading, # from POST /api/v1/styleme
"photoBase64": photo_base64,
"occasion": "Weekend brunch",
}).json()
while True:
result = requests.post(BASE, headers=headers, json={
"action": "poll", "jobId": submitted["jobId"],
}).json()
if result["status"] in ("done", "error"):
break
time.sleep(2.5)
print(result) # {'ok': True, 'status': 'done', 'imageUrl': '...', 'outfitBreakdown': [...]}Building against a different source?