Cloud API
Hosted at api.vcp.name. Needs an API key from a Starter or Premium plan and uses the plan's monthly tokens.
OpenAPI spec · Base URL https://api.vcp.name
Generate speech, clone and design voices, change voices, transcribe audio, and make avatar videos, dubs and cuts.
Quickstart
Create a key in Settings > API at app.voicecreator.pro. The API needs a Starter or Premium plan.
curl -X POST https://api.vcp.name/v1/speech \
-H "Authorization: Bearer $VCP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from the API.", "voice": "af_alloy"}'
curl "https://api.vcp.name/v1/jobs/JOB_ID?wait=60" \
-H "Authorization: Bearer $VCP_API_KEY"
When the job succeeds, result.audio_url links to the WAV. GET /v1/voices lists voice ids.
Authentication
Send your key as a bearer token: Authorization: Bearer vcp_live_.... Keep it secret.
Jobs
Endpoints that generate media return 202 with a job. GET /v1/jobs/{id}?wait=60 returns when the job finishes, or after at most 60 seconds. Call it again while the status is queued or running, or use webhooks. Results are kept for 24 hours.
Files
Send audio as audio_file_id or audio_url, photos as photo_file_id or photo_url, and videos as video_file_id. To upload, call POST /v1/uploads, PUT the file to upload_url with the same Content-Type within 2 hours, then pass file_id. Uploads are kept for 24 hours.
| Type | Formats | Upload | Link |
|---|---|---|---|
| Audio | WAV, FLAC, Ogg/Opus, MP3 | 200 MB | 25 MB |
| Photo | JPEG, PNG, WEBP | 20 MB | 20 MB |
| Video | MP4, MOV, MKV, WEBM | 2 GB | Not supported |
Avatar audio can be up to 20 seconds.
Errors
Errors return an HTTP status and a body with a code, a message and, when one field is at fault, a param.
{ "error": { "code": "invalid_request", "message": "text is required.", "param": "text" } }
Retry 429, 500 and 503 with exponential backoff, waiting at least Retry-After when it is set. Send an Idempotency-Key so a retried POST cannot start a second job. Other errors fail again until the request changes.
Every response has a Request-Id header. Include it when you contact support.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter is missing or invalid. param names it. |
| 400 | invalid_json | The body is not JSON. |
| 400 | model_not_supported | The model cannot do this. |
| 400 | url_fetch_failed | The audio or photo link could not be downloaded. |
| 401 | unauthorized | The API key is missing, invalid or revoked. |
| 402 | out_of_tokens | The account is out of tokens for this period. |
| 403 | paid_plan_required | The API needs a Starter or Premium plan. |
| 403 | preset_read_only | Preset voices cannot be renamed or deleted. |
| 404 | not_found | No such resource. |
| 404 | voice_not_found | No such voice. |
| 404 | file_not_found | No such upload. Uploads expire after 24 hours. |
| 404 | speech_job_not_found | No such speech job, or its audio expired. |
| 405 | method_not_allowed | The path does not support this method. |
| 409 | idempotency_conflict | The Idempotency-Key was used with a different body. |
| 409 | job_not_ready | The job has not succeeded yet. |
| 409 | design_expired | The voice design is older than 24 hours. |
| 409 | voice_unavailable | The voice has no reference audio. |
| 409 | voice_save_failed | The voice could not be saved. |
| 413 | too_large | The file or text is too large. |
| 413 | storage_full | The account storage is full. |
| 422 | photo_refused | The photo cannot be used. |
| 429 | rate_limited | Too many requests for this key. |
| 429 | concurrency_limit | Too many jobs in progress. |
| 500 | internal_error | Something went wrong on our side. |
| 503 | unavailable | Temporarily unavailable. |
A failed job has status failed and an error with one of these codes:
| Code | Meaning |
|---|---|
out_of_tokens | The account ran out of tokens. |
no_speech | No speech was found, or none was produced. |
no_face | No face was found in the photo. |
many_faces | The photo shows more than one face. |
too_long | The video is longer than 30 minutes for a dub or 3 hours for a cut. |
unsupported_language | The model cannot speak the target language. |
bad_video | The video has no audio track or cannot be read. |
generation_failed | The job failed. Try again. |
Idempotency
POST requests that start a job accept an Idempotency-Key header. Retrying with the same key and body returns the original job. The same key with a different body returns 409 idempotency_conflict. Keys are kept for 90 days.
Rate limits
| Limit | Starter | Premium |
|---|---|---|
| Requests per minute, per key | 60 | 60 |
| Jobs in progress | 3 | 10 |
Over a limit, requests get 429 with a Retry-After header. Responses to a valid key have a RateLimit-Policy header.
Webhooks
Add up to 5 https endpoints in Settings > API. When a job made with an API key finishes, every enabled endpoint gets a POST with job.succeeded, job.failed or job.cancelled. data is the job.
Requests are signed with HMAC-SHA256 in the webhook-id, webhook-timestamp and webhook-signature headers of Standard Webhooks. Verify them with your whsec_ secret and the raw request body:
import { Webhook } from 'standardwebhooks'
const event = new Webhook(process.env.VCP_WEBHOOK_SECRET).verify(rawBody, headers)
import os
from standardwebhooks.webhooks import Webhook
event = Webhook(os.environ["VCP_WEBHOOK_SECRET"]).verify(raw_body, headers)
verify fails when the signature is wrong or the timestamp is more than 5 minutes off.
Respond with a 2xx within 10 seconds. Redirects are not followed. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 8 hours and 12 hours, up to 7 attempts over about 23 hours. An endpoint that fails for 3 days is turned off. Use webhook-id to skip duplicates. Events can arrive out of order.
Usage
Jobs use tokens from your plan. tokens_charged on the job shows the cost. API requests stay out of History. List them in Settings > API or with GET /v1/requests.