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} returns status: "FAILED"
  • errorMessage is NOT_CONVERTIBLE: <code> — the NOT_CONVERTIBLE: prefix distinguishes a refusal (the photos cannot produce one product) from a generation failure (errorMessage without 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