Calling a Try-On API From Node.js: A Worked Example
By the ZeDaq.ai team · · 4 min read
On this page
This worked example is for calling a try-on API from Node.js: base64-encode the shopper's photo and your product image, send them in a JSON POST to the ZeDaq.ai /api/v1/tryon endpoint with an x-api-key header using fetch, and poll if you get a 202. No extra packages are needed for the core call.
This worked example builds on the virtual try-on API overview. If you prefer another language, see calling a try-on API from Python or the PHP version. The endpoint is also in the API reference.
What do you need first?
- A ZeDaq.ai API key from Dashboard → API keys, stored in
ZEDAQ_API_KEY. - A Node.js version with built-in
fetch. - A person photo and a garment image, as JPEG or PNG.
The docs say a full-length photo around 800px wide works well.
What does the call return?
| Status | Meaning |
|---|---|
200 |
Finished. The body has image_base64 and mime_type. |
202 |
Still rendering. The body has job_id and poll_url. |
| Other | An error in the form { "error": "code", "message": "..." }. |
Usage is only charged once a render succeeds.
How do you write the function?
This uses ES modules, so save it as tryon.mjs.
import { readFile, writeFile } from "node:fs/promises";
const BASE = "https://www.zedaq.ai";
const headers = {
"x-api-key": process.env.ZEDAQ_API_KEY,
"Content-Type": "application/json",
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const b64 = async (path) => (await readFile(path)).toString("base64");
export async function tryOn(personPath, productPath, garmentType = "upper_body") {
let res = await fetch(`${BASE}/api/v1/tryon`, {
method: "POST",
headers,
body: JSON.stringify({
person_image_base64: await b64(personPath),
product_image_base64: await b64(productPath),
garment_type: garmentType,
}),
});
let data = await res.json();
for (let i = 0; i < 30 && res.status === 202; i++) {
await sleep(2000);
res = await fetch(BASE + data.poll_url, { headers });
data = await res.json();
}
if (res.status !== 200) {
throw new Error(`${data.error ?? res.status}: ${data.message ?? "try-on failed"}`);
}
return data;
}
const result = await tryOn("person.jpg", "shirt.png");
await writeFile("result.png", Buffer.from(result.image_base64, "base64"));
Why poll every 2 seconds?
The API reference tells you to poll the poll_url every 2 seconds until it returns 200. The loop above caps the wait at 30 tries so a stuck job cannot run forever.
How do you wrap it in an Express endpoint?
Your page should call your own route, so the key stays on the server. Here is a sketch:
import express from "express";
import multer from "multer";
import { writeFile } from "node:fs/promises";
import { tryOn } from "./tryon.mjs";
const app = express();
const upload = multer({ dest: "uploads/" });
app.post("/tryon", upload.fields([{ name: "person" }, { name: "product" }]), async (req, res) => {
try {
const out = await tryOn(req.files.person[0].path, req.files.product[0].path);
res.json({ image_base64: out.image_base64, mime_type: out.mime_type });
} catch (err) {
res.status(502).json({ error: err.message });
}
});
app.listen(process.env.PORT);
Delete the uploaded files after each request in a real app. For key storage rules, read using an x-api-key header safely.
How do you call the page route from the browser?
Your front end posts the two images to your Express route and shows the result. Convert the returned base64 string into an image source.
const form = new FormData();
form.append("person", personFile);
form.append("product", productBlob);
const res = await fetch("/tryon", { method: "POST", body: form });
const { image_base64, mime_type } = await res.json();
document.querySelector("#result").src = `data:${mime_type};base64,${image_base64}`;
Show a loading state during the call, since a render may take several polls. If the route returns an error, show a plain message and let the shopper try again.
How do you test the whole flow?
- Run the script once with a clear, full-length photo and a plain product image.
- Check that
result.pngshows the garment on the person. - Try a poor photo, such as a cropped one, and see how your error handling reads.
- Unset the key on purpose and confirm that your code reports an authentication error instead of crashing.
- Read the response codes in the API reference and map each to a message for shoppers.
What should you check before going live?
- The key comes from an environment variable and is never sent to the browser.
- The polling loop has a limit.
- Shoppers see a loading state while a
202is pending. - Uploaded files are removed after use.
- Your size charts are set up if you also use sizing. See how to write size guides.
Make your first try-on request from Node.js
FAQs
Do I need axios or node-fetch?
No, if your Node.js version has built-in fetch. Any HTTP client works the same way.
Can I call the API from React in the browser?
Keep the key on your server and let React call your own route. Browser code exposes the key.
What does the garment_type field do?
It is optional and takes upper_body, lower_body or dresses. It tells the render which part of the body the garment covers.
What does a 202 response mean?
The render is still running. Poll the poll_url until it returns 200 with the image.
Try ZeDaq.ai free
Virtual try-on and Find my size for your store, plus client, order and invoicing tools for tailors, in one subscription.


