# Generate personal speech

`POST https://api.vidmoat.com/v1/speech/generations`

- Scope: `speech.generate`
- Cost: Configured credits per started 1,000 Unicode characters

Submit a script using an authorised ready personal voice. The complete script is chunked without dropping text. Returns a durable job ID; poll instead of resubmitting.

> **Note.** Idempotency-Key is required (8 to 128 printable characters). Identical retries return the original job without charging twice. Changed inputs with the same key return 409. Failed/cancelled jobs refund exactly once; intentionally starting a fresh paid retry requires a new key.

### Body

- `voiceId` (string, required): Your ready voice ID.
- `text` (string, required): 1 to 8,000 Unicode characters; output capped at ten minutes.
- `language` (enum, optional, default `en`): Only tested, installed languages are accepted. One of `en`.
- `speed` (number, optional, default `1`): Pitch-preserving speaking pace, 0.75 to 1.25.
- `expression` (number, optional): Optional delivery intensity, 0 to 1. Supported only when capabilities.settings.expression exists. Subtle .25, balanced .5, animated .75; no guarantee of named emotions. Unsupported controls are rejected.
- `projectId` (string, optional): Optional owned/member project receiving the audio. The generated output is authorised for that project; the voice reference stays private.

## Response

Returns `202`.

> **Tip: With a test key.** Test keys cannot consume credits or run inference.

## Errors

Besides the errors any request can get ([authentication](https://developer.vidmoat.com/developer/docs/authentication#when-a-request-is-refused), [rate limits](https://developer.vidmoat.com/developer/docs/rate-limits)):

| Status | Code | When |
| --- | --- | --- |
| 409 | [`conflict`](https://developer.vidmoat.com/developer/docs/errors#conflict) | The idempotency key belongs to different inputs, or the job is not in the required state. |
| 503 | [`unavailable`](https://developer.vidmoat.com/developer/docs/errors#unavailable) | Inference, private storage, configured pilot billing or the limiter is unavailable. Nothing new is charged before admission. |

## Request examples

```bash curl
curl --fail-with-body "https://api.vidmoat.com/v1/speech/generations" -H "Authorization: Bearer $VIDMOAT_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: $REQUEST_ID" -d '{"voiceId":"VOICE_ID","text":"Every story starts with a voice.","language":"en","speed":1}'
```

```js Node
const res = await fetch('https://api.vidmoat.com/v1/speech/generations', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.VIDMOAT_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    voiceId: 'VOICE_ID',
    text: 'Every story starts with a voice.',
    language: 'en',
    speed: 1,
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? `HTTP ${res.status}`);
console.log(data);
```

```python Python
import os
import requests

res = requests.post(
    "https://api.vidmoat.com/v1/speech/generations",
    headers={"Authorization": f"Bearer {os.environ['VIDMOAT_KEY']}"},
    json={
      "voiceId": "VOICE_ID",
      "text": "Every story starts with a voice.",
      "language": "en",
      "speed": 1,
    },
    timeout=60,
)
res.raise_for_status()
print(res.json())
```

## Example response

```json
{
  "id": "11111111-1111-4111-8111-111111111111",
  "voiceId": "22222222-2222-4222-8222-222222222222",
  "kind": "generate",
  "status": "queued",
  "refunded": false,
  "pipeline": "openvoice-v2-melo-1",
  "result": null,
  "error": null
}
```

---

Source: https://developer.vidmoat.com/developer/docs/api/speech/generate
Previous: [List speech history](https://developer.vidmoat.com/developer/docs/api/speech/list-generations.md)
Next: [Get speech status](https://developer.vidmoat.com/developer/docs/api/speech/get-generation.md)
All documentation: https://developer.vidmoat.com/llms-full.txt
