Two-Person Photo
Description
Provide two images to generate a two-person photo and receive the processed image. Processing is asynchronous by default; query the result using the task ID returned on submission.
Version
1.0
Image Requirements
Provide two images per request, each accessible through a URL.
Request URL
Production host: https://openapi.meitu.com
Task submission endpoint:
https://openapi.meitu.com/api/v1/sdk/sync/pushTask name (task): /v1/couples_photo/default/488650
Task type (task_type): formula
HTTP Method
POST
Content-Type: application/json
Authentication
Use an AK/SK pair with access to this capability and sign the request using the signing SDK. See API Request Signing.
Request Parameters
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | task | string | Fixed value: /v1/couples_photo/default/488650. |
| Yes | task_type | string | Fixed value: formula. |
| Yes | init_images | object[] | Two input images. |
| Yes | params | string | Inference parameters. Send an empty string, "", to use defaults. |
| No | sync_timeout | integer | Default: -1. Return task information immediately without waiting for generation to finish, then retrieve the result through the task-query API. |
init_images Element
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | url | string | Accessible image URL. |
| No | profile | object | Image property configuration. |
profile
When providing profile, include the following field:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | media_profiles | object | Media properties. |
media_profiles
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | media_data_type | string | Default: url, indicating that the image is supplied by URL. |
params
This API accepts params: "" to use default parameters. Keep it as an empty string rather than an object, null, or the string "{}". The source does not list configurable inference parameters.
Request Example
Replace both input image URLs:
{
"task": "/v1/couples_photo/default/488650",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/person-1.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
},
{
"url": "https://example.com/person-2.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
}
],
"params": "",
"sync_timeout": -1
}Response Fields
code=0 and error_code=0 indicate normal request processing, not task completion. Check data.status: read data.result.urls when it is 10, or continue querying when it is 9. Download and save result images promptly.
| Field | Type | Description |
|---|---|---|
| request_id / trace_id | string | Request ID and trace ID. |
| code | integer | Response status code; 0 indicates normal request processing. This is not the HTTP status code. |
| error_code | integer | Error code; 0 means no error. |
| message | string | Response message; success in the success example. |
| data | object / null | Task details; may be null on failure. |
data
| Field | Type | Description |
|---|---|---|
| status | integer | -1: not found; 0: created; 1: running; 2: failed; 9: query required; 10: succeeded. |
| result | object | Task result. |
| progress | number | Task progress, range 0–1; 1 means complete. |
| predict_elapsed | number | Estimated processing time in milliseconds. |
| create_time | number | Task creation timestamp in milliseconds. |
| task_id | string | Gateway task ID used for queries. |
| custom_task_id / trace_id / client_info | string | Custom task ID, trace ID, and client information. |
| init_images | array / null | Input media information; null in the success example. |
result
| Field | Type | Description |
|---|---|---|
| id | string | Task identifier, matching data.task_id in the example. |
| urls / images | string[] | Result image URLs; both lists are identical in the example. |
| parameters | object | Output parameters; may contain rsp_media_type, where url means URL output. Empty in the example. |
| data | object | Detailed algorithm response. |
| msg / msg_id | string | Algorithm message and message ID; msg_id is not the task ID used for queries. |
| mtlab_res | object | Underlying algorithm compatibility result. |
| media_info_list | object[] | Output media information. |
result.data
| Field | Type | Description |
|---|---|---|
| duration | object | Stage timestamps and timings, detailed below. |
| error_code / error_msg | integer / string | Algorithm error code and message; error_code=0 indicates success. |
| extra | object | Additional information. |
| media_info_list | object[] | Result media list. |
| msg_id | string | Algorithm message ID. |
| parameter | object | Returned parameters. |
result.data.duration
| Field | Type | Description |
|---|---|---|
| alg_process_time | number | Algorithm processing time in milliseconds. |
| created_timestamp / pull_timestamp | number | Creation and resource-pull timestamps in seconds. |
| upload_time / waiting_time | number | Upload and queue-wait times in milliseconds. |
| repost_time | number | Described as retry time in the source; units are not specified. |
media_info_list Element
Applies to both data.result.media_info_list and data.result.data.media_info_list.
| Field | Type | Description |
|---|---|---|
| media_data | string | Result image URL. |
| media_extra | object | Additional media information. |
| media_profiles | object | Media properties. |
| media_profiles.media_data_type | string | url indicates that media_data contains a URL. |
result.mtlab_res
| Field | Type | Description |
|---|---|---|
| ErrorCode / error_code | integer | Underlying error code and compatibility field. |
| ErrorMsg / error_msg | string | Underlying error message and compatibility field. |
| media_info_list | array / null | Media information; null in the success example. |
| msg_id | string | Algorithm message ID. |
| parameter | object / null | Underlying parameters; null in the success example. |
Response Examples
The success and failure structures follow the source documentation. Task IDs, algorithm message IDs, and image URLs are illustrative replacements, not live results. The success example shows a completed task; it does not mean the default asynchronous submission returns a finished image immediately.
Success Example
{
"request_id": "",
"trace_id": "",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 10,
"result": {
"id": "example-task-id",
"urls": [
"https://example.com/couples-photo.png"
],
"parameters": {},
"data": {
"duration": {
"alg_process_time": 2396,
"created_timestamp": 1775548648,
"pull_timestamp": 1775548648,
"repost_time": 0,
"upload_time": 74,
"waiting_time": 58
},
"error_code": 0,
"error_msg": "success",
"extra": {},
"media_info_list": [
{
"media_data": "https://example.com/couples-photo.png",
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
}
],
"msg_id": "example-algorithm-msg-id",
"parameter": {}
},
"msg": "success",
"msg_id": "example-algorithm-msg-id",
"images": [
"https://example.com/couples-photo.png"
],
"mtlab_res": {
"ErrorCode": 0,
"ErrorMsg": "",
"error_code": 0,
"error_msg": "",
"media_info_list": null,
"msg_id": "example-algorithm-msg-id",
"parameter": null
},
"media_info_list": [
{
"media_data": "https://example.com/couples-photo.png",
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
}
]
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1775548616543,
"task_id": "example-task-id",
"custom_task_id": "",
"trace_id": "",
"client_info": "",
"init_images": null
}
}Query Required Example
The following illustrates a condensed response requiring a query. After asynchronous submission or an incomplete synchronous wait, save data.task_id and use Task Query. Do not resubmit or use msg_id for the query.
{
"request_id": "",
"trace_id": "",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 9,
"result": {
"id": "example-task-id"
},
"progress": 0,
"task_id": "example-task-id"
}
}Error Example
The source error example uses HTTP 400:
{
"error_code": 20001,
"message": "PROCESS_ERROR",
"data": null
}Common Error Codes
20001 / PROCESS_ERROR indicates a processing error. See API Error Codes for other errors.
SDK Examples
Each example embeds the complete request body; no separate JSON file is required. Set up the SDK for your language, then replace the AK, SK, and both input image URLs. All examples preserve params="" and sync_timeout=-1.
Each example submits once and prints the response. Use the task-query API if processing is incomplete. Sign and send the same URL, method, and request body.
Python
import json
import requests
from sign_sdk import sign
def main():
key = "YOUR_ACCESS_KEY"
secret = "YOUR_SECRET_KEY"
url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
payload = {
"task": "/v1/couples_photo/default/488650",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/person-1.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
},
{
"url": "https://example.com/person-2.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
}
],
"params": "",
"sync_timeout": -1
}
body = json.dumps(payload, ensure_ascii=True)
headers = {
"Content-Type": "application/json",
sign.HeaderHost: "openapi.meitu.com",
}
signer = sign.Signer(key, secret)
signed_request = signer.sign(url, "POST", headers, body)
with requests.Session() as session:
response = session.send(
signed_request, timeout=(10, 60), verify=True, allow_redirects=False
)
print("Status:", response.status_code)
print("Response:", response.text)
if __name__ == "__main__":
main()
Go
package main
import (
"fmt"
"io"
"net/http"
"time"
"github.com/formula/api/signer"
)
func main() {
body := `{
"task": "/v1/couples_photo/default/488650",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/person-1.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
},
{
"url": "https://example.com/person-2.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
}
],
"params": "",
"sync_timeout": -1
}`
url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
headers := make(http.Header)
headers.Set("Content-Type", "application/json")
headers.Set(signer.HeaderHost, "openapi.meitu.com")
signObj := signer.NewSigner("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY")
req, err := signObj.Sign(url, http.MethodPost, headers, body)
if err != nil {
fmt.Println("Sign request failed:", 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("Send request failed:", err)
return
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Read response failed:", err)
return
}
fmt.Println("Status:", resp.StatusCode)
fmt.Println("Response:", string(responseBody))
}
PHP
<?php
require_once __DIR__ . '/signer.php';
$body = json_encode([
'task' => '/v1/couples_photo/default/488650',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://example.com/person-1.png',
'profile' => [
'media_profiles' => [
'media_data_type' => 'url'
]
]
],
[
'url' => 'https://example.com/person-2.png',
'profile' => [
'media_profiles' => [
'media_data_type' => 'url'
]
]
]
],
'params' => '',
'sync_timeout' => -1
], JSON_UNESCAPED_SLASHES);
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$headers = [
'Content-Type' => 'application/json',
'Host' => 'openapi.meitu.com',
];
$signer = new Signer('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY');
$curl = $signer->sign($url, 'POST', $headers, $body);
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
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 body =
"{\n" +
" \"task\": \"/v1/couples_photo/default/488650\",\n" +
" \"task_type\": \"formula\",\n" +
" \"init_images\": [\n" +
" {\n" +
" \"url\": \"https://example.com/person-1.png\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"url\": \"https://example.com/person-2.png\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"params\": \"\",\n" +
" \"sync_timeout\": -1\n" +
"}";
String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put(Signer.HeaderHost, "openapi.meitu.com");
Signer signer = new Signer("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY");
Map<String, String> signedHeaders = signer.sign(url, "POST", headers, body);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
try {
connection.setRequestMethod("POST");
connection.setConnectTimeout(10000);
connection.setReadTimeout(60000);
connection.setInstanceFollowRedirects(false);
connection.setDoOutput(true);
for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
connection.setFixedLengthStreamingMode(bodyBytes.length);
try (OutputStream output = connection.getOutputStream()) {
output.write(bodyBytes);
}
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();
}
}
}