Virtual Try-On API
Send a photo of a person and a photo of a garment — get back a photorealistic image of that person wearing that garment. One synchronous call, powered by Google Gemini.
You need an API key to get started
API keys are issued manually. Contact us to request a key — tell us a bit about your use case and expected volume, and we'll set you up with a key and pricing that fits. Keys are shown once at creation time, so store yours securely.
Key concepts
Read these first — they shape how you integrate.
- Synchronous & long-running. The request does everything in one call — fetches your two images, runs the AI generation, stores the result, and returns the final image URL. There is no queue, job id, or polling.
- Generation takes ~10–60 seconds. The server keeps the connection alive up to 120s. Raise your HTTP client's read timeout (e.g. 180s) or it will give up before the image is ready.
- You send image URLs, not file uploads. Both input images must be publicly reachable over
http/https. The server downloads them itself. - Output is a hosted PNG URL. The API never returns base64 — you get a permanent URL to the generated
.png.
Authentication
Every request requires a Bearer API key in the Authorization header:
Authorization: Bearer bnto_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx- Keys look like
bnto_live_<random>. - Keys are shown once, at creation time — store yours securely.
- A missing, malformed, revoked, or unknown key returns
401. - Treat the key as a secret: never embed it in client-side/browser code or commit it to source control. Call the API from your backend.
Request
Send a JSON body to the endpoint with your two image URLs.
Body schema:
{
"model_name": "tryon-v1.6",
"inputs": {
"model_image": "https://example.com/person.jpg",
"garment_image": "https://example.com/garment.jpg"
}
}| Field | Type | Required | Description |
|---|---|---|---|
model_name | string | No | Logical model identifier. Defaults to tryon-v1.6. Echoed back in the response. |
inputs.model_image | URL | Yes | Public URL of the person/model photo. Defines the output's identity, pose, background, and aspect ratio. |
inputs.garment_image | URL | Yes | Public URL of the garment photo. Used as a reference for fabric, cut, and color only — its background is ignored. |
model_image is the person; garment_image is the clothing. The person's face, body, pose, and background are preserved; the garment is transferred onto them.Image requirements
Both images must satisfy all of the following, or the request fails with image_fetch_failed (400):
| Requirement | Limit / Rule |
|---|---|
| Scheme | http or https only |
| Format | PNG, JPEG, WebP, or GIF |
| Content-Type | The server must receive an image/* content type |
| Max size | 10 MB per image |
| Reachability | URL must return HTTP 200 within 10 seconds |
| Redirects | At most 2 redirects are followed |
| Host | Must resolve to a public IP address |
169.254.169.254), 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1, and fc00::/7. Every redirect hop is re-validated. Use public image hosting (your own CDN, object storage with public read, etc.).Output aspect ratio
The output's aspect ratio matches the model_image, snapped to the nearest supported ratio (e.g. 1:1, 16:9, 9:16, 4:3, 3:4, 4:5). For predictable framing, crop your person photo to the aspect ratio you want before submitting.
Tips for best results
- Use a clear, well-lit, front-facing photo of the person.
- Use a clean garment reference (flat-lay or on a model) where the item is fully visible. The garment's background is discarded, so it doesn't need to be plain.
- If the garment image includes accessories (shoes, hats, belts, glasses, bags, jewelry), they may also be applied.
- A full-body garment (dress/jumpsuit) replaces both top and bottom; a "top" is layered appropriately.
Response
Success → 200 OK
{
"id": "tryon_V1StGXR8Z5jdHi6BmyT8x",
"model_name": "tryon-v1.6",
"output": {
"image_url": "https://pub-xxxx.r2.dev/outputs/tryon_V1StGXR8Z5jdHi6BmyT8x.png"
},
"created_at": "2026-06-21T12:00:00Z"
}| Field | Description |
|---|---|
id | Unique try-on id (tryon_…). Also the output filename. |
model_name | The model identifier you sent (or the default). |
output.image_url | Public URL of the generated PNG. |
created_at | UTC timestamp (ISO 8601). |
X-Request-ID header — include it when reporting issues. You can also set your own X-Request-ID on the request and it will be echoed back.Errors
All errors share one envelope:
{ "error": { "code": "invalid_api_key", "message": "Invalid API key." } }Validation errors (422) additionally include a details array describing which fields failed.
| HTTP | code | Meaning / cause |
|---|---|---|
| 401 | missing_api_key | No / malformed Authorization header |
| 401 | invalid_api_key | Unknown or revoked key |
| 422 | validation_error | Request body failed schema validation |
| 422 | generation_failed | The model could not produce an image |
| 422 | content_blocked | Generation blocked by safety filters |
| 400 | image_fetch_failed | An input image could not be fetched/validated |
| 429 | rate_limited | Per-key rate limit exceeded. Includes a Retry-After header |
| 502 | storage_failed | Generated image could not be stored |
| 500 | internal_error | Unexpected server error |
Handling guidance
429— read theRetry-Afterresponse header (seconds) and back off before retrying.generation_failed/content_blocked— usually input-related. Try a clearer person photo or a different garment; retrying an identicalcontent_blockedrequest rarely helps.image_fetch_failed— verify the URL is public, returns an image, is under 10 MB, and resolves to a public host.internal_error/storage_failed— transient; safe to retry with backoff.
Rate limits
- Default: 60 requests per minute per API key (some keys may have a custom limit).
- Enforced with a rolling 60-second window.
- Exceeding it returns
429with aRetry-Afterheader telling you how many seconds to wait.
Examples
The generation can take up to ~60s — every example sets a generous client timeout (~180s) so the connection stays open until the image is ready.
curl --max-time 180 -X POST https://api.benitoai.com/v1/tryon \
-H "Authorization: Bearer bnto_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"model_name": "tryon-v1.6",
"inputs": {
"model_image": "https://example.com/person.jpg",
"garment_image": "https://example.com/garment.jpg"
}
}'Other endpoints
| Endpoint | Purpose |
|---|---|
GET /health | Liveness check. Returns 200 {"status":"ok","mongo":true} when healthy, 503 otherwise. No auth required. |
GET /docs | Interactive Swagger/OpenAPI UI for exploring and testing the API in the browser. |
Quick checklist before you call
- ☐ Bearer API key set in the
Authorizationheader. - ☐ Both image URLs are public
http/httpsand return a PNG/JPEG/WebP/GIF. - ☐ Each image is under 10 MB and loads within 10s.
- ☐ Person photo cropped to your desired output aspect ratio.
- ☐ HTTP client read timeout set to ~180s.
- ☐ Retry/backoff logic for
429(honorRetry-After) and5xx.
Ready to start?
Contact us to get your API key and go live.