2D to 3D
The Youzu 3D Lens Component Transforms 2D product images into interactive 3D models.
API
The 2D to 3D API allows you to convert 2D product images into interactive 3D models. This is an asynchronous process that requires polling for job completion.
Authentication
Use the Authorization: Bearer YOUR_TOKEN header with a single-use token obtained from /api/v1/token. Generate a 2d-to-3d:create token for creation and a new 2d-to-3d:read token for every status request.
Create 2D to 3D Conversion
Endpoint: POST /api/v1/2d-to-3d
Convert 2D images to 3D models. Send every photo you have of the product in images, in any order — listing galleries, room shots and close-ups included. The service works out which product the listing is about, picks the cleanest front view, cuts a single unit out of group shots, removes props, and fills the back, side and three-quarter views from the photos whose angle it can establish; views it cannot establish with confidence are left empty rather than mislabelled. Listings the service cannot turn into one product — a mix of different products, no product in any photo, only close-ups — are refused asynchronously: the job is created normally and then fails; see Refusals below.
Three quality tiers are available through modelType: standard (fastest; front and back views), premium (front, back, side and three-quarter views), and ultra (highest quality — materials such as fleece, leather and corrugated board come out as surface detail rather than flat colour; 7–15 minutes per job; priced above premium).
The named view fields (frontImage, backImage, leftImage, rightImage and their *Url variants) are deprecated. They still work, but every image is pooled with images and re-judged, so naming a view no longer changes the result.
curl -X POST "https://platform.youzu.ai/api/v1/2d-to-3d" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "images=@product-front.jpg" \
-F "images=@product-side.jpg" \
-F "images=@product-back.jpg"
// Convert 2D images to 3D model using token
const formData = new FormData();
formData.append('images', document.getElementById('image1').files[0]);
formData.append('images', document.getElementById('image2').files[0]);
fetch('https://platform.youzu.ai/api/v1/2d-to-3d', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN'
},
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Conversion started:', data);
// Poll for completion using data.id
})
.catch(error => {
console.error('Error:', error);
});
import requests
# Convert 2D images to 3D model using token
url = 'https://platform.youzu.ai/api/v1/2d-to-3d'
headers = {
'Authorization': 'Bearer YOUR_TOKEN'
}
files = [
('images', open('product-front.jpg', 'rb')),
('images', open('product-side.jpg', 'rb'))
]
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
job = response.json()
print('Conversion started:', job)
# Poll for completion using job['id']
else:
print('Error:', response.status_code, response.text)
Get Conversion Status
Endpoint: GET /api/v1/2d-to-3d/{id}
Check the status of a 2D to 3D conversion job.
curl -X GET "https://platform.youzu.ai/api/v1/2d-to-3d/YOUR_JOB_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
// Check conversion status using token
fetch('https://platform.youzu.ai/api/v1/2d-to-3d/YOUR_JOB_ID', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_TOKEN'
}
})
.then(response => response.json())
.then(data => {
console.log('Job status:', data.status);
if (data.status === 'COMPLETED') {
console.log('3D model URL:', data.outputUrls[0]);
}
})
.catch(error => {
console.error('Error:', error);
});
import requests
# Check conversion status using token
url = 'https://platform.youzu.ai/api/v1/2d-to-3d/YOUR_JOB_ID'
headers = {
'Authorization': 'Bearer YOUR_TOKEN'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
job = response.json()
print('Job status:', job['status'])
if job['status'] == 'COMPLETED':
print('3D model URL:', job['outputUrls'][0])
else:
print('Error:', response.status_code, response.text)
SDK
Refusals
POST /api/v1/2d-to-3d validates only the request itself synchronously (authentication, at least one image, a known modelType); those failures come back as an immediate HTTP error. Whether the photos can be turned into one product is decided later, by the worker, so a refusal is never a synchronous error — it arrives through polling:
GET /api/v1/2d-to-3d/{id}returnsstatus: "FAILED"errorMessageisNOT_CONVERTIBLE: <code>— theNOT_CONVERTIBLE:prefix distinguishes a refusal (the photos cannot produce one product) from a generation failure (errorMessagewithout the prefix, e.g. a model outage), which is worth retrying; a refusal is not — the same photos produce the same verdict.
errorMessage |
Meaning | What to change |
|---|---|---|
NOT_CONVERTIBLE: MULTIPLE_PRODUCTS |
The photos show different products (a sofa and an ottoman, chairs and a table), or every photo shows several units and none could be separated | Submit one product per job, or add a photo with one unit alone |
NOT_CONVERTIBLE: NO_PRODUCT_DETECTED |
No photo shows a product the service could isolate | Add a photo with the product clearly in frame |
NOT_CONVERTIBLE: INCOMPLETE_PRODUCT |
Every usable photo cuts part of the product off (only close-ups, legs out of frame) | Add a photo with the whole product visible |
NOT_CONVERTIBLE: ALL_INSTANCES_OCCLUDED |
Every unit is partly hidden behind another (a tightly packed row) | Add a photo of one unit standing clear |
NOT_CONVERTIBLE: OBJECT_TOO_SMALL |
The product is too small in every photo to reconstruct without upscaling | Add a closer, higher-resolution photo |
NOT_CONVERTIBLE: TOO_FINE_FOR_RESOLUTION |
The product's structure (thin rods, wire) is finer than the photo resolution can carry | Add a higher-resolution photo |
NOT_CONVERTIBLE: MATTE_UNRELIABLE |
The product could not be separated cleanly from its background in any photo | Add a photo against a plain background |
A refusal costs no generation credit; only the image analysis ran.
Two further terminal outcomes exist once generation has started. errorMessage: "GENERATION_REJECTED: <message>" means the reconstruction step could not build a model from these images; different photos may succeed. errorMessage: "GENERATION_FAILED: <message>" means generation ended without a model for a reason unrelated to the photos: a transient failure, or the service being temporarily unavailable. ultra is generated once — a transient failure ends the job rather than retrying it, because a retry would purchase a second full run; standard and premium retry transient failures automatically before reporting one. The text after each prefix is a fixed, human-readable sentence suitable for showing to end users; it never carries provider or model detail. Both outcomes are final for that job; submit again to try again.
Props
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
alt |
alt |
The alt text for the image | string | '' |
height |
height |
The height of the component | string | undefined |
modelSrc |
model-src |
The 3D model URL (.gib file) | string | undefined |
showLoader |
show-loader |
Manually control the loading state (for demo purposes) | boolean | false |
src |
src |
The source URL of the image | string | undefined |
width |
width |
The width of the component | string | undefined |
Events
| Event | Description | Type |
|---|---|---|
viewChange |
Event emitted when view mode changes (2D to 3D or vice versa) | CustomEvent<ViewChangeEventData> |
Methods
| Method | Parameters | Return Type | Description |
|---|---|---|---|
switchTo3D |
options?: LensOptions |
Promise<void> |
Switch to 3D view |
toggleView |
Promise<void> |
Toggle between 2D and 3D views |