Lazybird Platform

API Reference

List Lazybird voices and generate MP3 speech from text or SSML in your own application.

Overview

The Lazybird Speech API lets you list available voices, generate MP3 speech from text or SSML, and create, edit and publish cloned voices.

Base URL:

Text
1https://api.lazybird.app/v2

/v1 keeps working for existing integrations. It differs only in GET /voices, which there returns one unpaged list of the first 1,000 library voices (?owner=me for your own).

A machine-readable OpenAPI 3.1 description of every endpoint is at www.lazybird.app/openapi.json.

Authentication

There are two ways to get an API key:

  • Create one in your Lazybird Studio settings.
  • Request one by email, without opening the studio. This is built for AI agents; see Get an API key without the UI.

Send the key with each request.

Preferred header:

HTTP
1X-API-Key: YOUR_API_KEY

You can also pass the key as an api_key query parameter when a tool cannot set headers:

Text
1https://api.lazybird.app/v2/voices?api_key=YOUR_API_KEY

Keep API keys private. Do not expose them in browser code, mobile apps, public repositories, or client-side logs.

Get an API key without the UI (for agents)

An agent can sign up or log in with just an email address. Lazybird emails a 6-digit code; the agent (or you, reading the code back to it) trades the code for a new API key. If no account exists for the email, a free account is created.

1. Request a code

HTTP
1POST /auth/request-code
Terminal
1curl -X POST "https://api.lazybird.app/v2/auth/request-code" \2  -H "Content-Type: application/json" \3  -d '{"email":"you@example.com"}'

Response (202 Accepted):

JSON
1{2  "sent": true,3  "expiresInSeconds": 6004}

The response is the same whether or not an account exists for the email. Use a permanent inbox; disposable email domains are rejected with 400. You can request one code per email per minute and five per hour; extra requests get 429 with a Retry-After header.

2. Verify the code

HTTP
1POST /auth/verify-code
Field Type Required Description
email string Yes The email the code was sent to
code string Yes The 6-digit code from the email
keyName string No A label for the new key, up to 100 characters
Terminal
1curl -X POST "https://api.lazybird.app/v2/auth/verify-code" \2  -H "Content-Type: application/json" \3  -d '{"email":"you@example.com","code":"123456","keyName":"my agent"}'

Response:

JSON
1{2  "apiKey": "YOUR_API_KEY",3  "userId": "abc123",4  "email": "you@example.com",5  "isNewAccount": true6}

Codes expire after 10 minutes and work once. A wrong or expired code returns 400 with {"error": "Invalid or expired code"}; after five wrong attempts the code is cancelled and you need to request a new one. Repeated wrong codes for one email, or too many attempts or new accounts from one network or one email domain, return 429 with a Retry-After header. Company and custom domains can create up to 15 new accounts a day this way. A 429 for too many new accounts leaves the code valid, so you can retry before it expires. Each successful verification creates a new key; existing keys keep working.

Store the key somewhere private, such as an environment variable. Accounts created this way start on the free plan; see Check your account and Upgrade your plan.

Agent skill

When you are using an AI coding agent, install the official Lazybird API skill so the agent has the current API shape, examples, and integration gotchas available in context.

Install the Lazybird API skill
1npx skills add lazybirdai/skills --agent codex --skill lazybird-api

Quickstart

Generate speech with a known voice ID:

Generate speech
1curl -X POST "https://api.lazybird.app/v2/generate-speech" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --output speech.mp3 \5  --data '{6    "voiceId": "tgJ2Hes6DauwTg8kFrRPLN",7    "text": "My name is Elyssia and I am a voice from Lazybird."8  }'

The response is an MP3 audio file. Save the response body to a file instead of trying to display it as JSON.

Check service status

HTTP
1GET /status

Returns the current API health.

Example:

Terminal
1curl "https://api.lazybird.app/v2/status"

Response:

JSON
1{2  "status": "ok"3}

Check your account

HTTP
1GET /me

Returns the account the API key belongs to, its plan and credit usage for the current period.

Terminal
1curl "https://api.lazybird.app/v2/me" \2  -H "X-API-Key: YOUR_API_KEY"

Response:

JSON
1{2  "userId": "abc123",3  "email": "you@example.com",4  "plan": {5    "tier": "free",6    "name": "Free",7    "isPaid": false8  },9  "credits": {10    "used": 1200,11    "limit": 10000,12    "extraCredits": 0,13    "remaining": 8800,14    "resetsAt": "2026-11-01T09:30:00.000Z"15  }16}

plan.tier is one of free, creator, pro, max, or legacy for older plans. remaining is the plan allowance left this period plus any purchased extraCredits. resetsAt is when used goes back to zero; it is omitted for team seats. One credit is one character. The plan allowance is used first; purchased extraCredits never expire.

Upgrade or buy credits

HTTP
1POST /checkout

Creates a checkout link for a paid plan, or for a one-time pack of extra credits. Open the link in a browser to pay; the plan or credits apply to the account as soon as payment completes, and the same API key keeps working.

Field Type Required Description
plan string For a plan creator, pro or max
interval string For a plan monthly or yearly
creditsUsd number For credits Whole dollars to spend on credits, from 10 to 1000

Extra credits cost $30 per 1M with no plan, $20 per 1M on Creator and $5 per 1M on Pro or Max, so {"creditsUsd":10} buys 333,333 credits with no plan or 2,000,000 on Pro. They never expire. Without a plan they are for personal use; plans include commercial rights.

Terminal
1curl -X POST "https://api.lazybird.app/v2/checkout" \2  -H "X-API-Key: YOUR_API_KEY" \3  -H "Content-Type: application/json" \4  -d '{"plan":"creator","interval":"monthly"}'5 6curl -X POST "https://api.lazybird.app/v2/checkout" \7  -H "X-API-Key: YOUR_API_KEY" \8  -H "Content-Type: application/json" \9  -d '{"creditsUsd":10}'

Response:

JSON
1{2  "checkoutUrl": "https://..."3}

See pricing for what each plan includes.

List voices

HTTP
1GET /voices

Returns the voices you can use with generate-speech, in two sections:

  • myVoices: your own voices (your clones, and any you have published)
  • library: the public voice library

Example:

Terminal
1curl "https://api.lazybird.app/v2/voices?language=en&gender=female" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"

Query parameters (all optional):

Parameter Description
q Search text, matched against voice names, descriptions and labels
language A locale such as en-US, or a language such as en for all of its locales
gender female or male
labels Comma-separated labels; a voice must carry all of them
sort recommended (default, also when searching), name, usage or recent
limit Voices per section, 1 to 100. Default 100.
section myVoices or library: return only that section and page through it (see below)
cursor The nextCursor of the previous page, together with section

Response (abbreviated):

JSON
1{2  "myVoices": [],3  "library": [4    {5      "id": "tgJ2Hes6DauwTg8kFrRPLN",6      "displayName": "Elyssia",7      "description": "YouTube Host",8      "language": "en-US",9      "gender": "female",10      "previewText": "Hey everyone, I'm Elyssia.",11      "avatar": "https://cdn.lazybird.app/..."12    }13  ],14  "totals": {15    "myVoices": 0,16    "library": 58017  }18}

totals is the number of matching voices in each section. Use the id value as voiceId when generating speech.

Page through a section

Add section to get one section, limit voices at a time. While more voices match, the response includes nextCursor; send it back as cursor with the same filters and sort to get the next page.

Terminal
1curl "https://api.lazybird.app/v2/voices?section=library&cursor=NEXT_CURSOR" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"
JSON
1{2  "myVoices": [],3  "library": [],4  "nextCursor": "eyJ0IjoicmVjb21tZW5kZWQiLCJpZCI6..."5}

The last page has no nextCursor. If the voice a cursor points at is removed, the request fails with 400; start again from the first page.

Generate speech

HTTP
1POST /generate-speech

Generates speech with the selected voice and returns an MP3 audio file.

Headers:

Header Required Value
X-API-Key Yes Your Lazybird API key
Content-Type Yes application/json

Body:

Field Type Required Description
voiceId string Yes Voice ID from /voices
text string No Plain text to convert to speech
ssml string No SSML content. Use this instead of text when you need pauses or speech markup.
settings object No Optional voice settings for this generation

Provide either text or ssml.

Plain text example

Terminal
1curl -X POST "https://api.lazybird.app/v2/generate-speech" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --output speech.mp3 \5  --data '{6    "voiceId": "tgJ2Hes6DauwTg8kFrRPLN",7    "text": "My name is Elyssia and I am a voice from Lazybird."8  }'

SSML example

Terminal
1curl -X POST "https://api.lazybird.app/v2/generate-speech" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --output speech.mp3 \5  --data '{6    "voiceId": "tgJ2Hes6DauwTg8kFrRPLN",7    "ssml": "<speak>My name is Elyssia <break time=\"1s\"/> and I am a voice from Lazybird.</speak>"8  }'

Voice settings example

All settings fields are optional.

JSON
1{2  "voiceId": "tgJ2Hes6DauwTg8kFrRPLN",3  "text": "This line is slower and slightly higher pitched.",4  "settings": {5    "style": "sad",6    "speed": 0.7,7    "pitch": 1.2,8    "volume": 1,9    "styleIntensity": 1,10    "locale": "es-ES"11  }12}

Settings:

Field Type Description
style string Speaking style or tone, when supported by the selected voice
speed number Speech speed. 1 is normal speed. Must be greater than 0.
pitch number Voice pitch. 1 is the default pitch. Must be greater than 0.
volume number Output volume. 1 is the default volume. Must be 0 or greater (0 is silent).
styleIntensity number Strength of the selected speaking style
locale string BCP-47 locale override, such as en-US, es-ES, fr-FR, de-DE, or ja-JP, when supported by the selected voice

Locale override

Use settings.locale to choose the spoken language or accent for multilingual voices. This is especially important for cloned voices, which can be synthesized in different languages by passing a locale override.

Terminal
1curl -X POST "https://api.lazybird.app/v2/generate-speech" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --output spanish.mp3 \5  --data '{6    "voiceId": "tgJ2Hes6DauwTg8kFrRPLN",7    "text": "Hola, este audio usa una configuración regional en español.",8    "settings": {9      "locale": "es-ES"10    }11  }'

List your voices

HTTP
1GET /voices?section=myVoices

Returns only your own voices: private cloned voices and any you have published, paged like the library (limit, cursor).

Example:

Terminal
1curl "https://api.lazybird.app/v2/voices?section=myVoices" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"

Response (abbreviated):

JSON
1{2  "myVoices": [3    {4      "id": "abc123",5      "displayName": "Narrator",6      "scope": "user",7      "published": false,8      "mine": true9    }10  ],11  "library": []12}

Use the id as voiceId when generating speech. Treat it as opaque. Voices cloned before the unified voice list also accept their legacy pv: id anywhere a voice ID is accepted.

Create a voice

HTTP
1POST /voices

Creates a private cloned voice from a voice sample. Voice cloning requires a plan with voice cloning access and available voice slots.

Upload the sample file as multipart/form-data:

Field Type Required Description
name text Yes Display name for the cloned voice
sample file Yes Voice sample: MP3, WAV, or OGG, up to 20 MB. Use a clean 10–30 second recording of one speaker.
Terminal
1curl -X POST "https://api.lazybird.app/v2/voices" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -F "name=Narrator" \4  -F "sample=@sample.wav"

If the sample is already hosted at a publicly reachable URL, send JSON instead:

Field Type Required Description
name string Yes Display name for the cloned voice
sampleUrl string Yes Public URL for the source voice sample
Terminal
1curl -X POST "https://api.lazybird.app/v2/voices" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --data '{5    "name": "Narrator",6    "sampleUrl": "https://example.com/sample.wav"7  }'

Send either a sample file or a sampleUrl, not both. A successful request returns 201 Created with the new voice:

JSON
1{2  "voice": {3    "id": "abc123",4    "displayName": "Narrator",5    "scope": "user",6    "published": false,7    "mine": true8  }9}

Design a voice

HTTP
1POST /voices/design

Generates a new voice from a text description of who is speaking and returns a sample of it speaking text. The description is never spoken. Requires voice cloning access.

Field Type Required Description
description string Yes Who is speaking: age, gender, pitch, energy, accent, emotion. Up to 500 characters.
text string Yes What the sample says. Up to 1000 characters. Each design uses characters equal to the length of text, with a minimum of 50.
language string No Locale of text, such as en-US or vi-VN. Detected when omitted.
seed integer No 0–2147483647. Omit for a new random voice; reuse a returned seed with the same description and text to reproduce it.
Terminal
1curl -X POST "https://api.lazybird.app/v2/voices/design" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --data '{5    "description": "A youthful, energetic, enthusiastic American woman",6    "text": "Hey there! I am so excited to show you what we built this week."7  }'
JSON
1{2  "sampleUrl": "https://cdn.lazybird.app/users/.../voice-design-....mp3",3  "seed": 1359090529,4  "duration": 4.825}

Designing does not create a voice or use a voice slot. To keep a design, pass its sampleUrl to Create a voice:

Terminal
1curl -X POST "https://api.lazybird.app/v2/voices" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --data '{5    "name": "Energetic Host",6    "sampleUrl": "https://cdn.lazybird.app/users/.../voice-design-....mp3"7  }'

Generate speech with a cloned voice

Cloned voices work with the normal speech endpoint. Pass the voice id as voiceId:

Terminal
1curl -X POST "https://api.lazybird.app/v2/generate-speech" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --output cloned.mp3 \5  --data '{6    "voiceId": "abc123",7    "text": "Este audio usa mi voz clonada en español.",8    "settings": {9      "locale": "es-ES"10    }11  }'

Get a voice

HTTP
1GET /voices/{voiceId}

Returns one voice: any voice in the public library, or one of your own private voices.

Terminal
1curl "https://api.lazybird.app/v2/voices/abc123" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"

Response:

JSON
1{2  "voice": {3    "id": "abc123",4    "displayName": "Narrator",5    "description": "Deep, calm narrator",6    "labels": ["Narrator", "Male"],7    "language": "en-US",8    "avatar": "https://cdn.lazybird.app/...",9    "previewText": "Hi, I'm a cloned voice. I can say anything you want me to say.",10    "previewUrl": "https://cdn.lazybird.app/....mp3",11    "scope": "user",12    "published": false,13    "mine": true14  }15}

Update a voice

HTTP
1PATCH /voices/{voiceId}

Changes the details of one of your own voices. Send only the fields you want to change. Edits to a published voice show in the public library right away.

Field Type Description
name string Display name
description string Short description shown on the voice card, such as the character or show
labels string[] Replaces the voice's labels. Allowed: Male, Female, Child, Young Adult, Adult, Elder, Narrator, Audiobook, Podcast, Character, Celebrity, Streamer, Advertising, Anime, ASMR, Customer Service, Documentary, Education, Horror, Kids, Meditation, News, Sleep, Storytelling, Trailer, Calm, Deep, Energetic
language string Primary language and accent as a locale, such as en-US, en-GB, es-MX or ja-JP. An empty string clears it.
avatarUrl string Public URL of a PNG, JPEG or WebP image to use as the avatar
Terminal
1curl -X PATCH "https://api.lazybird.app/v2/voices/abc123" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --data '{5    "description": "Deep, calm narrator",6    "labels": ["Narrator", "Male"],7    "language": "en-US"8  }'

To upload the avatar image from a file instead, send multipart/form-data with the image as avatar (PNG, JPEG or WebP, up to 5 MB). The other fields can go in the same form; send labels comma-separated:

Terminal
1curl -X PATCH "https://api.lazybird.app/v2/voices/abc123" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -F "avatar=@avatar.png" \4  -F "labels=Narrator,Male"

Avatars are cropped to a 200×200 square, so use a square image with the face centred. Send either an avatar file or an avatarUrl, not both. Unknown fields are rejected with 400.

The response is the updated voice, in the same { "voice": { ... } } shape as Get a voice. Changing language clears the voice's preview clip, because the preview line follows the language; render it again afterwards.

Replace a voice sample

HTTP
1PUT /voices/{voiceId}/sample

Clones one of your voices again from a new recording. The voice keeps its ID, name, description, labels, language and avatar, so existing projects and integrations keep working and speak with the new sample from then on. Use it when the original sample had background noise, music or a second speaker.

Upload the new sample as multipart/form-data:

Field Type Required Description
sample file Yes Voice sample: MP3, WAV, or OGG, up to 20 MB. Use a clean 10–30 second recording of one speaker.
rightsConfirmed boolean For published voices Confirms this is your own voice or that the speaker agreed to it being cloned
Terminal
1curl -X PUT "https://api.lazybird.app/v2/voices/abc123/sample" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -F "sample=@new-sample.wav"

Or send JSON with a publicly reachable sampleUrl instead of the file:

Terminal
1curl -X PUT "https://api.lazybird.app/v2/voices/abc123/sample" \2  -H "X-API-Key: $LAZYBIRD_API_KEY" \3  -H "Content-Type: application/json" \4  --data '{5    "sampleUrl": "https://example.com/new-sample.wav",6    "rightsConfirmed": true7  }'

Replacing a sample counts as one voice creation toward your plan's monthly limit, but does not use another voice slot. A published voice changes for everyone who uses it, so rightsConfirmed is required for those. The response is the updated voice, in the same { "voice": { ... } } shape as Get a voice. The old preview clip is cleared; render it again afterwards.

Publish a voice

HTTP
1POST /voices/{voiceId}/publish

Adds one of your private voices to the public voice library. Anyone can then find it, open its page at https://lazybird.app/voices/{voiceId} and generate speech with it. Give the voice a name, description, labels, language and avatar before publishing; they are what people see in the library.

Terminal
1curl -X POST "https://api.lazybird.app/v2/voices/abc123/publish" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"

The response is the voice, now with "scope": "community" and "published": true.

To take it out of the library again, use POST /voices/{voiceId}/unpublish. The voice stays in your own voices.

Render a voice preview

HTTP
1POST /voices/{voiceId}/preview

Returns the voice's short preview clip, the previewText line spoken in the voice. The first call renders it; later calls return the stored clip. Rendering is charged to the voice owner's credits.

Terminal
1curl -X POST "https://api.lazybird.app/v2/voices/abc123/preview" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"

Response:

JSON
1{2  "previewUrl": "https://cdn.lazybird.app/....mp3",3  "duration": 4.24}

duration is in seconds and is only included when this call rendered the clip.

Delete a voice

HTTP
1DELETE /voices/{voiceId}

Deletes one of your own voices. A published voice is also removed from the public voice library.

Terminal
1curl -X DELETE "https://api.lazybird.app/v2/voices/abc123" \2  -H "X-API-Key: $LAZYBIRD_API_KEY"

Response:

JSON
1{2  "deleted": true3}

Deprecated personal voice endpoints

The /personal-voices endpoints are deprecated. They keep working for existing integrations, but new code should use the /voices endpoints. Deprecated responses include a Deprecation header and a Link header that points to the replacement.

Deprecated Use instead
GET /personal-voices GET /voices?section=myVoices
POST /personal-voices POST /voices
DELETE /personal-voices/{voiceId} DELETE /voices/{voiceId}
POST /personal-voices/{voiceId}/generate-speech POST /generate-speech with voiceId

GET /personal-voices and POST /personal-voices still return the original { id, name, sampleUrl } shape. Their id is the legacy pv: id when the voice has one.

Responses

Successful generate-speech requests return an MP3 file.

HTTP
1HTTP/1.1 200 OK2Content-Type: audio/mpeg

Errors return JSON with an error message:

JSON
1{2  "error": "Authorization Error: Invalid API key"3}
Status Meaning
400 Invalid input, such as a missing field or a wrong or expired login code
401 Missing or invalid API key
422 Your plan blocked the request, for example you are out of credits or voice cloning slots; check GET /me
429 Too many requests; wait for the number of seconds in the Retry-After header (also in retryAfterSeconds)
503 The service is temporarily unavailable; try again later

If a request fails, check that your API key is valid, the voiceId exists, and the request includes either text or ssml.

Testing tips

  • Make API requests from a server environment, not browser code.
  • Start by calling /voices and copying a voice ID from the response.
  • Save generate-speech responses with --output speech.mp3.
  • Keep SSML valid XML. Escape quotation marks correctly when sending SSML inside JSON.