Swap

Remove face identities and replace them with user-supplied ones

You will need an API token to send HTTP requests. See Authentication for instructions.

Quick start

Firstly, you'll need to upload a target image. This is the "background" image where your faces/identities will be placed upon. Please note that we only support the following formats: WEBP, JPEG and PNG.

You can also supply a URL pointing to an image on the web, without uploading the image yourself. See Uploading a target image for more information.

Note down these fields from the response:

Response
{
  "image_id": "67762577...", // IMAGE_ID
  "faces": {
    "approved_faces": [1, 1], 
    "coordinates_list": [
      {
        "id": 0             // FACE_ID
        // ...
      },
      {
        "id": 1             // FACE_ID
        // ...
      }
    ]
  }
}

For more information about the data contained in coordinates_list and how to process it, see Coordinates. To understand how to use the image_id parameter, see Image ID.

You can also specify some advanced settings for extra control (such as hair replacement). For that, see Upload options.


You may then need to upload one or more to be swapped onto the target image. Keep in mind that you can only upload one face at a time, so it's fine to call the endpoint several times.

Note the following field in the response:

Response
{
  "identity_name": "1W38bSrE..." // IDENTITY_ID
}

You can also reuse identities that are already stored in your profile. More on this in the detailed section later.


To create a swapped image, you will have to call the endpoint once for each face you want to swap. This will start asynchronous processes that will be handled the next step.

This is an example of request containing all required fields for face with "id": 0 (zero):

Request
{
  "identity_name": "1W38bSrE...", // IDENTITY_ID
  "id_image": "67762577...",      // IMAGE_ID
  "id_face": "0",                 // FACE_ID as string
  "options": "{\"flag_replace_and_download\":true}"
}

Note that the flag_replace_and_download bit is required. This flag allows to skip the pick process and directly download the final image.

You can also specify some advanced settings for extra control (such as similarity and seed). For that, see Generation options.


Notification objects returned by this endpoint will inform you of the end of a swap process on our servers. We recommend sending a request every 3 seconds at most, to not incur in rate limiting.

Request
{
  "name_list": ["download"]
}

After the process is complete, you might see a similar response:

Response
{
  "notifications_list": [
    {
      "data": {
        "f": 0,                    // FACE_ID
        "id_image": "67762577...", // IMAGE_ID
        "link": "https://...",     
        "link_hd": "https://...",  
      },
      "name": "download",
    },
  ]
}

Where link and link_hd point to the final image with face data.f swapped, as well as all previously swapped faces. Faces are not swapped in order, so you should always check data.f and count that face as done while keeping track of other faces.

Uploading a target image

Be careful!

All target images are deleted automatically after 24 hours of being uploaded. This action cannot be undone.

You can upload any image containing up to 30 people. Their faces will be automatically detected by the API. Please note that we only support the following formats: WEBP, JPEG and PNG.

import requests

api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
target_path = "path_to_image"
options = {
    # Check the "Options" section for more information
    "flag_hair": True,
}


with open(target_path, "rb") as target:
    response = requests.post(
        api_url + "/consistent_identities/upload_target",
        headers={"Authorization": "Bearer " + access_token},
        data={"options": json.dumps(options)},
        files={
            "file": target
        },
    ).json()
    image_id = response.get("image_id")
    print("Created image ID: ", image_id)

Alternatively, you can also supply a full static URL pointing to an image and our API will fetch it for you and process it in the same way as an uploaded file.png

import requests

api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
target_url = "http://example.com/image.jpg"
options = {
    # Check the "Options" section for more information
    "flag_hair": True,
}


response = requests.post(
    api_url + "/consistent_identities/upload_target",
    headers={"Authorization": "Bearer " + access_token},
    data={
        "url": target_url,
        "options": json.dumps(options),
    },
).json()
image_id = response.get("image_id")
print("Created image ID: ", image_id)

Remember to check the approved_faces bitmask! If a face with FACE_ID equals to a number N is valid, its corresponding value in approved_faces[N] will be equals to 1. A face is considered invalid if it is detected but it also is too distorted or too low resolution to ensure a quality swap result.

In this example, the API marked the faces with ID 1 and 3 as invalid. The coordinates_list will only contain data for faces with ID 0 and 2.

Response
{
  "image_id": "67762577...", // IMAGE_ID
  "faces": {
    "approved_faces": [1, 0, 1, 0], 
    "coordinates_list": [ /* ... */ ],
    // ...
  },
  // ...
}

For more information about the data contained in coordinates_list and how to process it, see Coordinates.

Upload options

The options parameter in the request body must be formatted as a JSON-encoded string. It can contain the following parameters:

ParameterValuesDescription
flag_hairtrue or falseIf true, the model will also draw over the subject's hair.

Using identities

To swap a face, you don't necessarily need to upload a new identity. Existing identities can be listed using the gallery endpoint.

Response
{
  "data": [
    {
      "f": "1W38bSrE...", // IDENTITY_ID
      "l": "https://...", // thumbnail
      "t": "2026-01-20T17:56:41.037429",
      // ...
    },
  ],
}

You can still upload a fresh face with the appropriate endpoint:

import requests

api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
face_path = "path_to_picture"

with open(face_path, "rb") as face:
    response = requests.post(
        api_url + "/consistent_identities/upload_face",
        headers={"Authorization": "Bearer " + access_token},
        files={
            "file": face,
        },
    ).json()
    face_id = response.get("identity_name")
    print(face_id)

Starting a swap

Swap processes are always identified by the IMAGE_ID. That's why you're able to (and you must) send a request for each face you want swapped.

import json
import requests

api_url = "https://api.piktid.com/api"
access_token = "your_access_token"

image_id = "67762577..."
options = {
    "flag_replace_and_download": True,
    # You can include your custom watermark here (PRO only)
    # "watermark_url": "https://...",
}
swaps = {
    0: "1W38bSrE...",
    2: "4We79yLh...",
}

for face_id, identity_id in swaps.items():
    response = requests.post(
        api_url + "/consistent_identities/generate",
        headers={"Authorization": "Bearer " + access_token},
        json={
            "identity_name": identity_id,  # IDENTITY_ID
            "id_image": image_id,          # IMAGE_ID
            "id_face": face_id,            # FACE_ID
            "options": json.dumps(options),
        },
    ).json()

In this example the swaps: dict[int, str] dictionary contains pairs associating detected FACE_IDs (from the target upload response) to existing IDENTITY_IDs. Retrieval of existing identities is described in detail in Using identities.

Generation options

The options parameter in the request body must be formatted as a JSON-encoded string. It can contain the following parameters:

ParameterValuesDescription
prompt_strengthNumber from 0.0 to 1.0A lower number will make the output image more similar to the source/original image. A higher number will make the output more similar to the target identity. In the web app, it is named "Similarity".
transfer_hairBooleanIf set True, the source hairstyle will be transferred to the target. It works with a strength greater or equal than 0.5.
seedInteger numberSeed used for random number generation. Two requests with the same seed will have the same result. If undefined, it will be randomized server-side.

Fetching results

Be careful!

Notifications are automatically deleted after exactly 10 minutes (600 seconds) from their creation.

Results are shared to clients via "notifications". Clients are expected to poll for notifications and handle them in the allowed time limit before they are automatically dismissed by the API.

import time
import requests

api_url = "https://api.piktid.com/api"
access_token = "your_access_token"

image_id = "67762577..."


def process_notifications(response, image_id):
    for notification in response.get("notifications_list", []):
        if notification["name"] != "download":
            return None, None

        if notification["data"]["id_image"] != image_id:
            return None, None

        return notification["data"], notification["id"]

    return None, None


def delete_notification(notification_id):
    requests.delete(
        api_url + "/notification/delete_json",
        headers={"Authorization": "Bearer " + access_token},
        json={"id": notification_id},
    )


# Loops for 300 seconds, even though swapping faces generally
# takes much less time.
for _ in range(300):
    response = requests.post(
        api_url + "/consistent_identities/notification/read",
        headers={"Authorization": "Bearer " + access_token},
        json={"name_list": "download,error"},
    ).json()

    data, notification_id = process_notifications(response, image_id)
    if data is not None:
        # Delete the notification to avoid reading it again
        delete_notification(notification_id)

        print(f"Face with ID {data['f']} finished processing!")
        print(data["l"])
        break

    time.sleep(1)

Remember that you can always get all the images you have generated so far with the "gallery" endpoint:

Be careful!

All generated images are deleted automatically after 24 hours of being created. This action cannot be undone. Make sure to download them!

import json
import requests
from pprint import pprint

api_url = "https://api.piktid.com/api"
access_token = "your_access_token"

response = requests.post(
    api_url + "/consistent_identities/gallery",
    headers={"Authorization": "Bearer " + access_token},
).json()
images = response["data"]
pprint(images)