AI Product Images: Scenes, Cutouts, and Generation Tasks

Description

Retrieve recommended product-image scenes, remove product backgrounds, and submit scene-generation tasks. Query progress and results using the business task ID returned on submission.

Endpoints

APIMethodURL
Recommended scenesGEThttps://openapi.meitu.com/design/enterprise/ai_product/materials
Product cutoutPOSThttps://openapi.meitu.com/design/enterprise/ai_product/sod
Submit taskPOSThttps://openapi.meitu.com/design/enterprise/ai_product/create
Query taskGEThttps://openapi.meitu.com/design/enterprise/ai_product/query

Authentication

Use an AK/SK pair with access to this capability and sign requests with the SDK. See API Request Signing.

Place GET parameters in the URL query string and sign the complete URL. POST requests use Content-Type: application/json. The product-cutout example also includes X-Aigcp-Way: handler.

Call Sequence

  1. Retrieve recommended scenes and select a scene id.
  2. If you need to adjust the product region, call the cutout API for the result and its x, y, width, and height. Otherwise, omit mask_url on submission to use automatic background removal.
  3. Submit a generation task and save data.id from the response.
  4. Query the dedicated endpoint in this document using that id. process_status=2 means completed; 3 means failed.

Use data.id, not task_ids[] or scene_result_images[].task_id. This API does not use the generic task API's status values.

Request

GET https://openapi.meitu.com/design/enterprise/ai_product/materials

Query Parameters

ParameterTypeDescription
cursorstringPagination cursor. Omit on the first request; the default is empty. Use the previous response's data.cursor for subsequent requests.
countintegerRequested number of entries. The example uses 20; no default or maximum is specified in the source.

Example URL:

https://openapi.meitu.com/design/enterprise/ai_product/materials?count=20

Response Fields

code=0 indicates success; reqid identifies the request for troubleshooting. Read scenes from data.data and the pagination cursor from data.cursor. Treat the returned cursor as opaque; do not construct it yourself.

data.data Element

FieldTypeDescription
idinteger (64-bit)Scene material ID. Convert it to a string for material_id when submitting.
default_namestringScene name.
thumbnailobjectThumbnail with url, width, and height.
typestringType, such as ai_product_photo_scene.
ai_product_type / is_color_scene / can_image_videointegerProduct type, color-scene flag, and image/video support flag; can_image_video=0 means unsupported.
alg_params / extra / light_modestringReserved algorithm parameters, additional information, and lighting mode; may be empty.
max_ratio / min_rationumberMaximum/minimum ratio; the documented default is 0.
prompt_idstringAssociated prompt ID.
preview_v2objectPreview configuration.
portrait_style_category_ids / v2_category_ids / v2_style_category_idsarrayPortrait-style, category, and style-category ID lists.

Response Example

HTTP 200. This example follows the source structure. IDs, names, and URLs are illustrative and do not guarantee current material availability. Use the IDs returned by the actual scene-list request.

{
  "reqid": "example-request-id",
  "code": 0,
  "data": {
    "cursor": "cs-1-1",
    "data": [
      {
        "ai_product_type": 0,
        "alg_params": "",
        "can_image_video": 0,
        "default_name": "Example scene",
        "extra": "",
        "id": 1759202837959028,
        "is_color_scene": 0,
        "light_mode": "",
        "max_ratio": 0,
        "min_ratio": 0,
        "portrait_style_category_ids": [],
        "preview_v2": {
          "4592": {}
        },
        "prompt_id": "10893",
        "thumbnail": {
          "height": 1024,
          "url": "https://example.com/scene.jpeg",
          "width": 1024
        },
        "type": "ai_product_photo_scene",
        "v2_category_ids": [
          4592
        ],
        "v2_style_category_ids": [
          4606
        ]
      }
    ]
  }
}

2. Product Cutout

Description and Version

Remove the background from a product image and return the cutout URL, position, and dimensions. Version: 1.0.

Image Requirements

JPG and PNG are supported.

Request

POST https://openapi.meitu.com/design/enterprise/ai_product/sod

Headers: Content-Type: application/json, X-Aigcp-Way: handler, and the signing headers generated by the SDK.

Request Parameters

RequiredParameterTypeDescription
YesurlstringProduct image URL.

Request Example

{
  "url": "https://example.com/product.png"
}

Response Fields

FieldTypeDescription
reqidstringRequest ID.
codeinteger0 indicates success.
data.urlstringCutout result URL.
data.x / data.yintegerProduct-region starting coordinates.
data.width / data.heightintegerProduct-region dimensions in pixels.

To construct box1, combine these values in x,y,width,height order. The example below gives "1,0,286,550".

Response Example

HTTP 200.

{
  "reqid": "example-request-id",
  "code": 0,
  "data": {
    "url": "https://example.com/cutout.png",
    "height": 550,
    "width": 286,
    "x": 1,
    "y": 0
  }
}

3. Submit a Task

Description and Version

Submit an AI product scene-generation task. Version: 1.0. JPG and PNG input images are supported.

Request

POST https://openapi.meitu.com/design/enterprise/ai_product/create

Content-Type: application/json

Request Parameters

Place all fields directly at the root of the JSON body.

RequiredParameterTypeDescription
YesurlstringOriginal product image URL.
Yesmaterial_idstringScene material ID from the scene list's id; separate multiple IDs with commas and send as one string.
Nomask_urlstringProduct mask URL. If omitted, background removal is performed automatically.
Nobox1stringOriginal product region in x,y,width,height format, using the cutout coordinates and dimensions. Omit if no position or size adjustment is needed.
Nobox2stringProduct bounding box on the canvas, in x,y,w,h format. Automatically centered if omitted; values may contain decimals.
Noproduct_categorystringProduct category in English. Default: product.
Nocallback_urlstringPOST callback URL for receiving task status.
Noscene_typenumber1: recommended scene, requires material_id; 2: custom scene, requires prompt. Default: 1.
ConditionalpromptstringCustom scene description, required when scene_type=2.
Nocanvas_wnumberCanvas width. Default: 512; range: 50–1024; step: 0.5. Output width is twice the canvas width.
Nocanvas_hnumberCanvas height. Default: 512; range: 50–1024; step: 0.5. Output height is twice the canvas height.
NoanglenumberCounterclockwise rotation angle, range 0–360. Supply pre-rotation coordinates in box2.
Noflipnumber0: no flip; 1: horizontal; 2: vertical; 3: both horizontal and vertical.
Norefimage_urlstringReference image URL, applicable only when scene_type=2.
Noprompt_vs_refimagenumberText-versus-reference-image generation preference, range -1.0–1.0; only applies when scene_type=2.
Noguidance_scalenumberCreative relevance parameter, range 5.0–15.0; only applies when scene_type=2.
NostepsintegerSampling steps, integer range 15–35; only applies when scene_type=2.
Noprompt_negstringDescription of unwanted content; only applies when scene_type=2.

material_id remains required as specified in the source. Custom scenes also require prompt; the source does not explicitly allow omission of material_id for custom scenes.

Request Example

Generate a recommended scene with automatic background removal. Replace material_id with an ID actually returned by the scene list.

{
  "url": "https://example.com/product.png",
  "material_id": "1759202837959028",
  "scene_type": 1,
  "product_category": "product",
  "canvas_w": 512,
  "canvas_h": 512
}

For position and size adjustments, add mask_url, box1, and box2 as needed. Supply a suitable mask URL; do not assume an arbitrary image result is a mask solely because of a field name.

Response Fields

code=0 indicates successful submission, not completion. Save data.id and use the query endpoint below to obtain the final result.

FieldTypeDescription
idintegerBusiness task ID used in /query?id=....
process_statusinteger1: processing; 2: completed; 3: failed.
result_image_urlsstring[]Generated image URLs. Not available on submission and may be an empty array; obtain them through querying after completion.
result_imagesobject[]Result objects: string id, image url, and process_status=2 for a completed image.
scene_result_imagesobject[]Scene-grouped results: scene_id, urls, process_status, and task_id.
result_numintegerNumber of generated results.
task_idsarrayInternal task list, not the ID for /query.
materialsobject / nullScene material information.
url / mask_url / refimage_urlstringOriginal, mask, and reference image URLs.
box1 / box2stringOriginal and adjusted regions in x,y,width,height format.
canvas_w / canvas_hnumberCanvas dimensions.
angle / flip / scene_typenumberRotation, flipping, and scene type, as defined in the request parameters.
product_category / prompt / translate_promptstringProduct category, prompt, and translated prompt.
prompt_vs_refimage / guidance_scale / stepsnumberCorresponding generation parameters.
created_at / finished_at / updated_atintegerCreation, completion, and update timestamps.
client_id / uid / user_material_id / z_indexintegerClient, user, custom material, and layer-order information.

Response Example

HTTP 200. This condensed response shows a task still processing. A full response may contain additional fields listed above.

{
  "reqid": "example-request-id",
  "code": 0,
  "data": {
    "id": 46062184,
    "process_status": 1,
    "result_image_urls": [],
    "result_images": [],
    "scene_result_images": [],
    "result_num": 1,
    "task_ids": [
      995952
    ],
    "scene_type": 1,
    "canvas_w": 512,
    "canvas_h": 512,
    "url": "https://example.com/product.png"
  }
}

4. Query a Task

Request

GET https://openapi.meitu.com/design/enterprise/ai_product/query

Query Parameters

RequiredParameterTypeDescription
Yesidintegerdata.id returned by task submission.
https://openapi.meitu.com/design/enterprise/ai_product/query?id=46062184

Replace the example ID with the ID of your task; do not use an ID from task_ids.

Response Fields

The task-detail structure matches that of submission:

  • process_status=1: processing; query again later.
  • process_status=2: completed; read result_image_urls.
  • process_status=3: failed.

result_images contains individual image IDs, URLs, and statuses. scene_result_images groups results by scene. Do not resubmit simply because a task is still processing.

Response Example

HTTP 200, with a condensed completed-task response:

{
  "reqid": "example-request-id",
  "code": 0,
  "data": {
    "id": 46062184,
    "process_status": 2,
    "result_image_urls": [
      "https://example.com/result.jpeg"
    ],
    "result_images": [
      {
        "id": "995952-0",
        "url": "https://example.com/result.jpeg",
        "process_status": 2
      }
    ],
    "result_num": 1,
    "scene_result_images": [
      {
        "scene_id": 10023,
        "urls": [
          "https://example.com/result.jpeg"
        ],
        "process_status": 2,
        "task_id": 995952
      }
    ],
    "scene_type": 1,
    "canvas_w": 512,
    "canvas_h": 512,
    "url": "https://example.com/product.png"
  }
}

Error Codes and Error Response

On failure, inspect code and message and retain reqid. The source error examples use HTTP 400.

CodeDescription
11001Invalid or missing parameters, such as material_id or a query ID.
11002Processing failure, such as a failed cutout.

Example submission failure:

{
  "reqid": "example-request-id",
  "code": 11001,
  "message": "Missing required parameter: material_id"
}

See API Error Codes for other errors.

These response examples are based on the source documentation, not live results from this work.

SDK Examples

Each language provides one complete example. Set operation to materials, sod, create, or query; each run calls only one endpoint. The default retrieves scenes. It does not automatically submit generation tasks or poll.

Set up the corresponding signing SDK, then replace the AK, SK, image URL, actual scene ID, or query ID. Request bodies are embedded; no external JSON files are required. For pagination, URL-encode the returned cursor, add it to the scene-list URL, and sign the final URL.

Python

Python Signing SDK

import json

import requests
from sign_sdk import sign

def build_request(operation):
    base = "https://openapi.meitu.com/design/enterprise/ai_product/"
    options = {
        "materials": ("GET", base + "materials?count=20", None),
        "sod": ("POST", base + "sod", {
            "url": "https://example.com/product.png"
        }),
        "create": ("POST", base + "create", {
            "url": "https://example.com/product.png",
            "material_id": "1759202837959028",
            "scene_type": 1,
            "product_category": "product",
            "canvas_w": 512,
            "canvas_h": 512
        }),
        "query": ("GET", base + "query?id=46062184", None),
    }
    method, url, payload = options[operation]
    body = "" if payload is None else json.dumps(payload, ensure_ascii=True)
    headers = {sign.HeaderHost: "openapi.meitu.com"}
    if method == "POST":
        headers["Content-Type"] = "application/json"
    if operation == "sod":
        headers["X-Aigcp-Way"] = "handler"
    signer = sign.Signer("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY")
    return signer.sign(url, method, headers, body)

def main():
    operation = "materials"  # materials, sod, create, query
    request = build_request(operation)
    with requests.Session() as session:
        response = session.send(
            request, timeout=(10, 60), verify=True, allow_redirects=False
        )
        print("Status:", response.status_code)
        print("Response:", response.text)

if __name__ == "__main__":
    main()

Go

Go Signing SDK

package main

import (
    "fmt"
    "io"
    "net/http"
    "time"

    "github.com/mtlab/api/signer"
)

func main() {
    operation := "materials" // materials, sod, create, query
    base := "https://openapi.meitu.com/design/enterprise/ai_product/"
    var method, url, body string
    headers := make(http.Header)
    headers.Set(signer.HeaderHost, "openapi.meitu.com")
    switch operation {
    case "materials":
        method, url = http.MethodGet, base+"materials?count=20"
    case "sod":
        method, url = http.MethodPost, base+"sod"
        body = `{"url":"https://example.com/product.png"}`
        headers.Set("X-Aigcp-Way", "handler")
    case "create":
        method, url = http.MethodPost, base+"create"
        body = `{"url":"https://example.com/product.png","material_id":"1759202837959028","scene_type":1,"product_category":"product","canvas_w":512,"canvas_h":512}`
    case "query":
        method, url = http.MethodGet, base+"query?id=46062184"
    default:
        fmt.Println("Unknown operation:", operation)
        return
    }
    if method == http.MethodPost {
        headers.Set("Content-Type", "application/json")
    }
    signObj := signer.NewSigner("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY")
    req, err := signObj.Sign(url, method, headers, body)
    if err != nil {
        fmt.Println("Sign error:", err)
        return
    }
    client := &http.Client{
        Timeout: 60 * time.Second,
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            return http.ErrUseLastResponse
        },
    }
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Request error:", err)
        return
    }
    defer resp.Body.Close()
    data, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Read error:", err)
        return
    }
    fmt.Println("Status:", resp.StatusCode)
    fmt.Println("Response:", string(data))
}

PHP

PHP Signing SDK

<?php
require_once __DIR__ . '/signer.php';

$operation = 'materials'; // materials, sod, create, query
$base = 'https://openapi.meitu.com/design/enterprise/ai_product/';
$options = [
    'materials' => ['GET', $base . 'materials?count=20', null],
    'sod' => ['POST', $base . 'sod', [
        'url' => 'https://example.com/product.png'
    ]],
    'create' => ['POST', $base . 'create', [
        'url' => 'https://example.com/product.png',
        'material_id' => '1759202837959028',
        'scene_type' => 1,
        'product_category' => 'product',
        'canvas_w' => 512,
        'canvas_h' => 512
    ]],
    'query' => ['GET', $base . 'query?id=46062184', null],
];
[$method, $url, $payload] = $options[$operation];
$body = $payload === null ? '' : json_encode(
    $payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
$headers = ['Host' => 'openapi.meitu.com'];
if ($method === 'POST') {
    $headers['Content-Type'] = 'application/json';
}
if ($operation === 'sod') {
    $headers['X-Aigcp-Way'] = 'handler';
}
$signer = new Signer('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY');
$curl = $signer->sign($url, $method, $headers, $body);
if ($method === 'GET') {
    curl_setopt($curl, CURLOPT_HTTPGET, true);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($curl);
if ($response === false) {
    echo 'Error: ' . curl_error($curl) . PHP_EOL;
} else {
    echo 'Status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
    echo 'Response: ' . $response . PHP_EOL;
}
curl_close($curl);

Java

Java Signing SDK

import com.meitu.openai.common.Signer;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws Exception {
        String operation = "materials"; // materials, sod, create, query
        String base = "https://openapi.meitu.com/design/enterprise/ai_product/";
        String method;
        String url;
        String body = "";
        Map<String, String> headers = new HashMap<>();
        headers.put(Signer.HeaderHost, "openapi.meitu.com");
        switch (operation) {
            case "materials":
                method = "GET";
                url = base + "materials?count=20";
                break;
            case "sod":
                method = "POST";
                url = base + "sod";
                body = "{\"url\":\"https://example.com/product.png\"}";
                headers.put("X-Aigcp-Way", "handler");
                break;
            case "create":
                method = "POST";
                url = base + "create";
                body = "{\"url\":\"https://example.com/product.png\",\"material_id\":\"1759202837959028\",\"scene_type\":1,\"product_category\":\"product\",\"canvas_w\":512,\"canvas_h\":512}";
                break;
            case "query":
                method = "GET";
                url = base + "query?id=46062184";
                break;
            default:
                throw new IllegalArgumentException("Unknown operation: " + operation);
        }
        if ("POST".equals(method)) {
            headers.put("Content-Type", "application/json");
        }
        Signer signer = new Signer("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY");
        Map<String, String> signedHeaders = signer.sign(url, method, headers, body);
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        try {
            connection.setRequestMethod(method);
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(60000);
            connection.setInstanceFollowRedirects(false);
            for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }
            if ("POST".equals(method)) {
                connection.setDoOutput(true);
                byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
                connection.setFixedLengthStreamingMode(bytes.length);
                try (OutputStream output = connection.getOutputStream()) {
                    output.write(bytes);
                }
            }
            int status = connection.getResponseCode();
            System.out.println("Status: " + status);
            InputStream responseStream = status >= 400
                    ? connection.getErrorStream() : connection.getInputStream();
            if (responseStream != null) {
                try (InputStream input = responseStream;
                     ByteArrayOutputStream output = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[4096];
                    int length;
                    while ((length = input.read(buffer)) != -1) {
                        output.write(buffer, 0, length);
                    }
                    System.out.println("Response: " + output.toString("UTF-8"));
                }
            }
        } finally {
            connection.disconnect();
        }
    }
}