Hairstyle Transfer
Description
Replaces the hairstyle in a portrait image using a specified hairstyle identifier and returns the processed image URLs.
Version
1.0
Image Requirements
Provide a portrait image URL. The example uses one image with media_data_type set to url.
Request URL
Production environment: https://openapi.meitu.com
Task submission endpoint: https://openapi.meitu.com/api/v1/sdk/sync/push
Task name (task): /v1/hairtransfer/495184
Task type (task_type): formulaHTTP Method
POST
Content-Type: application/json
X-Aigcp-Way: handler
Authentication
Obtain an Access Key and Secret Key, then sign the request as described in Open Platform API Signature. The signing SDK generates Authorization and X-Sdk-Date.
Request Parameters
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | task | string | Fixed value: /v1/hairtransfer/495184 |
| Yes | task_type | string | Fixed value: formula |
| Yes | init_images | object[] | Input portrait image list |
| Yes | params | string | Algorithm parameters encoded as a JSON string containing a parameter object |
| No | sync_timeout | int | Synchronous wait time in seconds. The example uses 30; -1 returns immediately without waiting. Use the returned task ID to query the result if processing is not complete |
Structure of each init_images item:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | url | string | Input portrait image URL |
| No | profile | object | Image metadata |
Structure of profile:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | media_profiles | object | Image properties; required when profile is provided |
| No | version | string | Version. Default: v1 |
Structure of media_profiles:
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | media_data_type | string | Media data type. Default: url, indicating an image URL |
params is a JSON string. Its decoded structure is:
{
"parameter": {
"rsp_media_type": "url",
"hair_type": "1"
}
}| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | parameter | object | Hairstyle processing parameters |
Structure of parameter:
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | rsp_media_type | string | Output media type. url returns image URLs |
| No | hair_type | string | Hairstyle identifier. 0 keeps the original hairstyle. For other identifiers, see Reference Hairstyles. Pass a string, such as "1" or "1_II" |
Request Example
This example uses hairstyle identifier "1". Replace the image URL and save the request body as request.json.
{
"task": "/v1/hairtransfer/495184",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/portrait.jpg",
"profile": {
"media_profiles": {
"media_data_type": "url"
},
"version": "v1"
}
}
],
"params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"hair_type\":\"1\"}}",
"sync_timeout": 30
}Response Fields
Success Response Fields
| Field | Type | Description |
|---|---|---|
request_id | string | Request ID |
trace_id | string | Trace ID |
code | int | Request status code. 0 indicates request success; use data.status to determine task completion |
error_code | int | Business error code. 0 indicates no business error |
message | string | Response message |
data | object | Task status and processing result |
Fields in data:
| Field | Type | Description |
|---|---|---|
status | int | Task status. 10 indicates success and 2 indicates failure. While processing is incomplete, retrieve updates with the task query API |
result | object | Processing result |
progress | number | Task progress from 0 to 1; 1 indicates completion |
predict_elapsed | number | Estimated processing time in milliseconds |
create_time | int64 | Task creation timestamp in milliseconds |
task_id | string | Task ID used to query the task |
custom_task_id | string | Custom task ID |
trace_id | string | Trace ID |
client_info | string | Client information |
init_images | object[]/null | Input image information |
Fields in data.result:
| Field | Type | Description |
|---|---|---|
id | string | Task ID |
urls | string[] | Result image URLs |
images | string[] | Result image URLs, equivalent to urls |
parameters | object | Result parameters |
data | object | Detailed algorithm result |
msg | string | Result message |
msg_id | string | Message ID |
mtlab_res | object | Algorithm response status |
media_info_list | object[] | Result image metadata |
Fields in data.result.data:
| Field | Type | Description |
|---|---|---|
duration | object | Timing information for processing stages |
error_code | int | Algorithm error code; 0 indicates success |
error_msg | string | Algorithm response message |
extra | object | Additional image conversion information |
media_info_list | object[] | Result image metadata |
msg_id | string | Message ID |
parameter | object | Algorithm result parameters |
Fields in data.result.data.duration:
| Field | Type | Description |
|---|---|---|
alg_process_time | number | Algorithm processing time in milliseconds |
created_timestamp | number | Task creation timestamp in seconds |
pull_timestamp | number | Resource retrieval timestamp in seconds |
repost_time | number | Retry duration |
upload_time | number | Upload time in milliseconds |
waiting_time | number | Queue wait time in milliseconds |
Fields in data.result.data.extra:
| Field | Type | Description |
|---|---|---|
trans_meta | object | Conversion metadata |
trans_meta.rsp_meta | object[] | Result image metadata list |
Fields in each rsp_meta item:
| Field | Type | Description |
|---|---|---|
height | number | Image height in pixels |
width | number | Image width in pixels |
media_type | string | Media type, such as image |
size | number | File size in KB |
Fields in each media_info_list item in the result:
| Field | Type | Description |
|---|---|---|
media_data | string | Result image URL |
media_extra | object/null | Additional image information |
media_profiles | object | Result image properties |
Fields in result media_profiles:
| Field | Type | Description |
|---|---|---|
media_data_size | number[] | Image dimensions in [height, width] order |
media_data_type | string | Media data type; url indicates a URL |
Fields in data.result.parameters and data.result.data.parameter:
| Field | Type | Description |
|---|---|---|
rsp_media_type | string | Output media type |
version | string | Algorithm version |
Fields in data.result.mtlab_res:
| Field | Type | Description |
|---|---|---|
ErrorCode | int | Algorithm error code |
ErrorMsg | string | Algorithm error message |
error_code | int | Compatibility error code field |
error_msg | string | Compatibility error message field |
media_info_list | object[]/null | Media information list |
msg_id | string | Message ID |
parameter | object/null | Algorithm parameter information |
Error Response Fields
| Field | Type | Description |
|---|---|---|
error_code | int | Error code |
message | string | Error message |
data | string/null | Additional error information; may be null |
Response Examples
Success Response Example
Response Status: 200
Content-Type: application/json; charset=utf-8
The example shows task information and output image URLs; additional result details are omitted.
{
"request_id": "req_example",
"trace_id": "trace_example",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 10,
"result": {
"id": "task_example",
"urls": [
"https://example.com/result_1.jpeg",
"https://example.com/result_2.jpeg"
],
"parameters": {
"rsp_media_type": "url",
"version": "1.0.32"
}
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1776137629570,
"task_id": "task_example",
"custom_task_id": "",
"trace_id": "trace_example",
"client_info": "",
"init_images": null
}
}Error Response Example
Response Status: 400
Content-Type: application/json; charset=utf-8
{
"error_code": 20001,
"message": "PROCESS_ERROR",
"data": null
}Task Query
When sync_timeout=-1, or the submission response does not contain the final result, use the returned data.task_id with the task query API.
GET https://openapi.meitu.com/api/v1/sdk/status?task_id=YOUR_TASK_ID
Query requests also require AK/SK signing. Continue querying while the query status is 0 (created) or 1 (processing). 10 means success, 2 means failure, and -1 means the task was not found. Tasks expire after 24 hours and cannot be queried after expiration.
API-Specific Error Codes
| ErrorCode | Message | Description |
|---|---|---|
| 20001 | PROCESS_ERROR | Processing failed |
Common Error Codes
See API Error Codes.
Calling Examples
Signing SDK integration guides: Go, Java, Python, JavaScript, PHP.
cURL
SDK_DATE is the UTC timestamp used for signing. SIGNATURE is the generated authorization value without the Bearer prefix. Both must correspond to the current request.json and the headers below. See Open Platform API Signature for generation instructions.
curl --request POST \
--url 'https://openapi.meitu.com/api/v1/sdk/sync/push' \
--header 'Host: openapi.meitu.com' \
--header 'Content-Type: application/json' \
--header 'X-Aigcp-Way: handler' \
--header "X-Sdk-Date: ${SDK_DATE}" \
--header "Authorization: Bearer ${SIGNATURE}" \
--data-binary @request.jsonPython
Download Python SDK 1.0.2, add the sign_sdk directory to the project, and install requests. Replace the AK, SK, and image URL before running.
import json
import requests
from sign_sdk import sign
access_key = "your_access_key"
secret_key = "your_secret_key"
url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
params = {
"parameter": {
"rsp_media_type": "url",
"hair_type": "1",
}
}
payload = {
"task": "/v1/hairtransfer/495184",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/portrait.jpg",
"profile": {
"media_profiles": {"media_data_type": "url"},
"version": "v1",
},
}
],
"params": json.dumps(params, separators=(",", ":")),
"sync_timeout": 30,
}
body = json.dumps(payload, separators=(",", ":"))
headers = {
"Host": "openapi.meitu.com",
"Content-Type": "application/json",
"X-Aigcp-Way": "handler",
}
signer = sign.Signer(access_key, secret_key)
request = signer.sign(url, "POST", headers, body)
with requests.Session() as session:
response = session.send(request, timeout=(10, 60))
print("Status:", response.status_code)
print("Response:", response.text)Go
Download Go SDK 1.0.3 and use Go 1.20 or later. Save the code below as demo.go in the extracted directory, replace the AK, SK, and image URL, then run go run .
package main
import (
"fmt"
"io"
"net/http"
"time"
"github.com/mtlab/api/signer"
)
func main() {
accessKey := "your_access_key"
secretKey := "your_secret_key"
endpoint := "https://openapi.meitu.com/api/v1/sdk/sync/push"
body := `{
"task": "/v1/hairtransfer/495184",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/portrait.jpg",
"profile": {
"media_profiles": {
"media_data_type": "url"
},
"version": "v1"
}
}
],
"params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"hair_type\":\"1\"}}",
"sync_timeout": 30
}`
headers := make(http.Header)
headers.Set("Host", "openapi.meitu.com")
headers.Set("Content-Type", "application/json")
headers.Set("X-Aigcp-Way", "handler")
sign := signer.NewSigner(accessKey, secretKey)
request, err := sign.Sign(endpoint, http.MethodPost, headers, body)
if err != nil {
fmt.Println("Sign request failed:", err)
return
}
client := &http.Client{Timeout: 60 * time.Second}
response, err := client.Do(request)
if err != nil {
fmt.Println("Send request failed:", err)
return
}
defer response.Body.Close()
responseBody, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println("Read response failed:", err)
return
}
fmt.Println("Status:", response.StatusCode)
fmt.Println("Response:", string(responseBody))
}PHP
Download PHP SDK 1.0.9, place signer.php in the same directory as the example, and enable the cURL extension.
<?php
require_once __DIR__ . '/signer.php';
$accessKey = 'your_access_key';
$secretKey = 'your_secret_key';
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$params = json_encode([
'parameter' => [
'rsp_media_type' => 'url',
'hair_type' => '1',
],
]);
$body = json_encode([
'task' => '/v1/hairtransfer/495184',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://example.com/portrait.jpg',
'profile' => [
'media_profiles' => ['media_data_type' => 'url'],
'version' => 'v1',
],
],
],
'params' => $params,
'sync_timeout' => 30,
]);
$headers = [
'Host' => 'openapi.meitu.com',
'Content-Type' => 'application/json',
'X-Aigcp-Way' => 'handler',
];
$signer = new Signer($accessKey, $secretKey);
$curl = $signer->sign($url, 'POST', $headers, $body);
curl_setopt_array($curl, [
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 60,
CURLOPT_HEADER => false,
]);
$response = curl_exec($curl);
if ($response === false) {
echo 'Error: ' . curl_error($curl);
} else {
echo 'Status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
echo 'Response: ' . $response . PHP_EOL;
}
curl_close($curl);Java
Download Java SDK 1.0.3 and use JDK 8 or later. Add src/main/java/com/meitu/openai/common/Signer.java from the package to the project and save the example as Main.java.
import com.meitu.openai.common.Signer;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
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 accessKey = "your_access_key";
String secretKey = "your_secret_key";
String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
String body = "{\n"
+ " \"task\": \"/v1/hairtransfer/495184\",\n"
+ " \"task_type\": \"formula\",\n"
+ " \"init_images\": [\n"
+ " {\n"
+ " \"url\": \"https://example.com/portrait.jpg\",\n"
+ " \"profile\": {\n"
+ " \"media_profiles\": {\n"
+ " \"media_data_type\": \"url\"\n"
+ " },\n"
+ " \"version\": \"v1\"\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"params\": \"{\\\"parameter\\\":{\\\"rsp_media_type\\\":\\\"url\\\",\\\"hair_type\\\":\\\"1\\\"}}\",\n"
+ " \"sync_timeout\": 30\n"
+ "}";
Map<String, String> headers = new HashMap<>();
headers.put("Host", "openapi.meitu.com");
headers.put("Content-Type", "application/json");
headers.put("X-Aigcp-Way", "handler");
Signer signer = new Signer(accessKey, secretKey);
Map<String, String> signedHeaders = signer.sign(url, "POST", headers, body);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setConnectTimeout(10_000);
connection.setReadTimeout(60_000);
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();
InputStream input = status >= 400
? connection.getErrorStream()
: connection.getInputStream();
System.out.println("Status: " + status);
if (input != null) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(input, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
connection.disconnect();
}
}