Create Act-Two voice swap
September 8, 2025
Table of contents
Runway Generate Video » Gen-4 or Gen-4 Turbo » Act-Two.
Please be aware that Runway’s moderation system analyzes your video assets and may fail task with moderation message if the content is determined to be offensive. Runway monitors the rate and the number of moderated tasks, which may result in your account being suspended when the internal threshold is exceeded.
The account you use to upload asset(s) via POST /assets will also be used to execute the generation. Uploading an asset to a specific account ensures that generation with that asset will occur under the same account.
https://api.useapi.net/v1/runwayml/gen4/act-two-voice
Request Headers
Authorization: Bearer {API token}
Content-Type: application/json
# Alternatively you can use multipart/form-data
# Content-Type: multipart/form-data
API token
is required, see Setup useapi.net for details.
Request Body
{
"video_assetId": "Required assetId of the video asset (3-30 seconds) for voice swap",
"voiceId": "Required voice ID from available voices",
"exploreMode": true,
"replyUrl": "Place your call back URL here",
"replyRef": "Place your reference id here",
"maxJobs": 5
}
-
video_assetId
is required. Specify the video asset (3-30 seconds duration) you want to apply voice swap to. Use GET /assets/?mediaType=video to see the list of video assets. To upload a new video asset use POST /assets. -
voiceId
is required. Specify the voice ID to use for voice swap. Use GET /lipsync/voices to see the list of available voices. -
exploreMode
is optional. Set totrue
if you have a Runway Unlimited plan and wish to execute relaxed generation. You are not charged credits for Explore mode generations. -
replyUrl
is optional, if not provided value from useapi.net account will be used.
Place here your callback URL. API will call the providedreplyUrl
once Runway task completed or failed.
Maximum length 1024 characters.
We recommend using sites like webhook.site to test callback URL functionality. -
replyRef
is optional, place here your reference id which will be stored and returned along with this Runway task response / result.
Maximum length 1024 characters. -
maxJobs
is optional, if not provided value for referenced by video assetId account will be used.
Valid range: 1…10.
Runway has dynamic query capacity and guarantees that, for a given account, at least one job will run — often two, and very rarely, three. If you have a single account linked, keep adding new jobs until you receive a429
response. Once you get a429
, wait for xx seconds or until at least one job completes, then try again. If you need to run more jobs in parallel, simply add more Runway accounts.
Responses
-
Use returned
taskId
to retrieve task status and results using GET /tasks/taskId
. The generated videourl
can be found in theartifacts
array of the task with the statusSUCCEEDED
.If you specify the optional parameter
replyUrl
the API will call the providedreplyUrl
with task progress updates until the task is complete or fails.{ "task": { "id": "<uuid>", "name": "<voice_name>_<video_name>", "image": null, "createdAt": "2025-09-08T06:52:26.378Z", "updatedAt": "2025-09-08T06:52:26.468Z", "taskType": "workflow_s2s_video", "options": { "name": "<voice_name>_<video_name>", "video": "<video url>", "voiceId": "<voice id>", "seconds": 15, "exploreMode": false, "assetGroupId": "<uuid>" }, "status": "PENDING", "error": null, "progressText": null, "progressRatio": "0", "estimatedTimeToStartSeconds": 0, "artifacts": [], "sharedAsset": null, "sourceAssetId": null, "taskId": "user:user_id-runwayml:account_email-task:task_uuid", "code": 200 } }
-
{ "error": "<Error message>", "code": 400 }
-
{ "error": "Unauthorized", "code": 401 }
-
{ "error": "Unable to retrieve assetId <uuid> (Not found.)", "code": 404 }
-
You do not have enough credits to run this task.
{ "error": "You do not have enough credits to run this task." }
-
Wait in a loop for at least 10..30 seconds and retry again.
There are two possible cases for API response 429:
- API query is full and can not accept new gen4/act-two-voice requests. Size of query defined by
maxJobs
optional parameter.
{ "error": "Account <Runway account email> is busy executing <Account maxJobs> tasks", "runningTasks": { "<Runway account email>": [ { "email": "<Runway account email>", "taskId": "user:user_id-runwayml:account_email-task:task_#1_uuid", "id": "<uuid>", "replyUrl": "<replyUrl if provided>", "replyRef": "<replyRef if provided>" }, { "email": "<Runway account email>", "taskId": "user:user_id-runwayml:account_email-task:task_#N_uuid", "id": "<uuid>", "replyUrl": "<replyUrl if provided>", "replyRef": "<replyRef if provided>" } ] }, "code": 429 }
- The API received an HTTP response status 429 from Runway. Runway has dynamic query management and may limit the number of simultaneously executed tasks based on internal service load and policies.
{ "error": "You have too many tasks running or pending. Please wait for some of them to finish before starting more." }
- API query is full and can not accept new gen4/act-two-voice requests. Size of query defined by
Model
{ // TypeScript, all fields are optional
task: {
taskId: string
id: string
name: string
image: any
createdAt: string
updatedAt: string
taskType: string
options: {
name: string
video: string
voiceId: string
seconds: number
exploreMode: boolean
assetGroupId: string
}
status: string
progressText: any
progressRatio: string
estimatedTimeToStartSeconds: number
artifacts: any[]
sharedAsset: any
sourceAssetId: any
error: {
errorMessage: string
reason: string
message: string
moderation_category: string
tally_asimov: boolean
}
code: number
}
}
Examples
-
curl -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer …" \ -X POST "https://api.useapi.net/v1/runwayml/gen4/act-two-voice" \ -d '{"video_assetId": "…", "voiceId": "…"}'
-
const video_assetId = "video asset (3-30 seconds) for voice swap"; const voiceId = "voice ID from available voices"; const apiUrl = `https://api.useapi.net/v1/runwayml/gen4/act-two-voice`; const token = "API token"; const data = { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }; data.body = JSON.stringify({ video_assetId, voiceId }); const response = await fetch(apiUrl, data); const result = await response.json(); console.log("response", {response, result});
-
import requests video_assetId = "video asset (3-30 seconds) for voice swap" voiceId = "voice ID from available voices" apiUrl = f"https://api.useapi.net/v1/runwayml/gen4/act-two-voice" token = "API token" headers = { "Content-Type": "application/json", "Authorization" : f"Bearer {token}" } body = { "video_assetId": f"{video_assetId}", "voiceId": f"{voiceId}" } response = requests.post(apiUrl, headers=headers, json=body) print(response, response.json())