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/push

Task 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

RequiredParameterTypeDescription
YestaskstringFixed value: /v1/couples_photo/default/488650.
Yestask_typestringFixed value: formula.
Yesinit_imagesobject[]Two input images.
YesparamsstringInference parameters. Send an empty string, "", to use defaults.
Nosync_timeoutintegerDefault: -1. Return task information immediately without waiting for generation to finish, then retrieve the result through the task-query API.

init_images Element

RequiredParameterTypeDescription
YesurlstringAccessible image URL.
NoprofileobjectImage property configuration.

profile

When providing profile, include the following field:

RequiredParameterTypeDescription
Yesmedia_profilesobjectMedia properties.

media_profiles

RequiredParameterTypeDescription
Nomedia_data_typestringDefault: 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.

FieldTypeDescription
request_id / trace_idstringRequest ID and trace ID.
codeintegerResponse status code; 0 indicates normal request processing. This is not the HTTP status code.
error_codeintegerError code; 0 means no error.
messagestringResponse message; success in the success example.
dataobject / nullTask details; may be null on failure.

data

FieldTypeDescription
statusinteger-1: not found; 0: created; 1: running; 2: failed; 9: query required; 10: succeeded.
resultobjectTask result.
progressnumberTask progress, range 0–1; 1 means complete.
predict_elapsednumberEstimated processing time in milliseconds.
create_timenumberTask creation timestamp in milliseconds.
task_idstringGateway task ID used for queries.
custom_task_id / trace_id / client_infostringCustom task ID, trace ID, and client information.
init_imagesarray / nullInput media information; null in the success example.

result

FieldTypeDescription
idstringTask identifier, matching data.task_id in the example.
urls / imagesstring[]Result image URLs; both lists are identical in the example.
parametersobjectOutput parameters; may contain rsp_media_type, where url means URL output. Empty in the example.
dataobjectDetailed algorithm response.
msg / msg_idstringAlgorithm message and message ID; msg_id is not the task ID used for queries.
mtlab_resobjectUnderlying algorithm compatibility result.
media_info_listobject[]Output media information.

result.data

FieldTypeDescription
durationobjectStage timestamps and timings, detailed below.
error_code / error_msginteger / stringAlgorithm error code and message; error_code=0 indicates success.
extraobjectAdditional information.
media_info_listobject[]Result media list.
msg_idstringAlgorithm message ID.
parameterobjectReturned parameters.

result.data.duration

FieldTypeDescription
alg_process_timenumberAlgorithm processing time in milliseconds.
created_timestamp / pull_timestampnumberCreation and resource-pull timestamps in seconds.
upload_time / waiting_timenumberUpload and queue-wait times in milliseconds.
repost_timenumberDescribed 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.

FieldTypeDescription
media_datastringResult image URL.
media_extraobjectAdditional media information.
media_profilesobjectMedia properties.
media_profiles.media_data_typestringurl indicates that media_data contains a URL.

result.mtlab_res

FieldTypeDescription
ErrorCode / error_codeintegerUnderlying error code and compatibility field.
ErrorMsg / error_msgstringUnderlying error message and compatibility field.
media_info_listarray / nullMedia information; null in the success example.
msg_idstringAlgorithm message ID.
parameterobject / nullUnderlying 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

Python Signing SDK

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

Go Signing SDK

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 Signing SDK

<?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

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 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();
        }
    }
}