VOICEHUE DEVELOPER API
One POST request turns text into audio: MP3 by default, or WAV on request. The endpoint follows the OpenAI speech format, so existing code usually needs only a new base URL and key. Every signed-in account can create 2 free keys with 10,000 characters a month each.
Sign in with a confirmed email address and open API keys in your dashboard. The full key is shown once: copy it and keep it on your server, never in browser code or a public repository.
Store the key in VOICEHUE_API_KEY and run one of these. The audio is saved as hello.mp3.
curl https://voicehue.com/v1/audio/speech \
-H "Authorization: Bearer $VOICEHUE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"voicehue-tts","input":"Hello from VoiceHue.","voice":"vh-es-alex"}' \
--output hello.mp3Your dashboard shows each key's characters used today and this month against its limits. A key stops working the moment you revoke it.
MCP server, OpenAPI and ready-to-paste configs
VoiceHue runs a remote MCP server at https://voicehue.com/mcp (Streamable HTTP). Add it to an AI assistant and the assistant can call text_to_speech to speak text and list_voices to pick a voice. It uses the same API keys and the same free plan as the HTTP API, and the audio comes back as MP3 (or WAV) in the tool result. The API is also described as OpenAPI 3.1 at /openapi.json, which automation tools can import. Replace vh_live_YOUR_KEY with a key from your dashboard.
Copy this prompt and paste it into your AI assistant. It explains VoiceHue and walks you through the setup for that tool. It contains no key.
In ChatGPT on the web, turn on developer mode under Settings, Security and login, then create an app with the server address https://voicehue.com/mcp and OAuth authentication. Sign in to VoiceHue when asked and allow the connection. This creates an API key named after the app; revoke it in your dashboard to end the access. Developer mode depends on your ChatGPT plan.
Add the remote server with one command; the key stays in an environment variable.
claude mcp add --transport http voicehue https://voicehue.com/mcp \ --header "Authorization: Bearer $VOICEHUE_API_KEY"
Add this to claude_desktop_config.json and restart. Claude Desktop reaches a remote server that needs a header through the mcp-remote bridge (needs Node.js).
{
"mcpServers": {
"voicehue": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://voicehue.com/mcp", "--header", "Authorization:${VOICEHUE_AUTH}"],
"env": {"VOICEHUE_AUTH": "Bearer vh_live_YOUR_KEY"}
}
}
}Add this to ~/.cursor/mcp.json (or .cursor/mcp.json in a project). Set VOICEHUE_API_KEY in your environment.
{
"mcpServers": {
"voicehue": {
"url": "https://voicehue.com/mcp",
"headers": {"Authorization": "Bearer ${env:VOICEHUE_API_KEY}"}
}
}
}Run this once. Codex reads the key from VOICEHUE_API_KEY.
codex mcp add voicehue --url https://voicehue.com/mcp --bearer-token-env-var VOICEHUE_API_KEY
Add this to .vscode/mcp.json. VS Code asks for the key once and stores it securely.
{
"inputs": [{"type": "promptString", "id": "voicehue-key", "description": "VoiceHue API key", "password": true}],
"servers": {
"voicehue": {
"type": "http",
"url": "https://voicehue.com/mcp",
"headers": {"Authorization": "Bearer ${input:voicehue-key}"}
}
}
}Add this to ~/.codeium/windsurf/mcp_config.json. Set VOICEHUE_API_KEY in your environment.
{
"mcpServers": {
"voicehue": {
"serverUrl": "https://voicehue.com/mcp",
"headers": {"Authorization": "Bearer ${env:VOICEHUE_API_KEY}"}
}
}
}Add this to ~/.gemini/settings.json. Set VOICEHUE_API_KEY in your environment.
{
"mcpServers": {
"voicehue": {
"httpUrl": "https://voicehue.com/mcp",
"headers": {"Authorization": "Bearer $VOICEHUE_API_KEY"}
}
}
}Point the official client at https://voicehue.com/v1 and use your VoiceHue key.
from openai import OpenAI
client = OpenAI(base_url="https://voicehue.com/v1", api_key="YOUR_API_KEY")
with client.audio.speech.with_streaming_response.create(
model="voicehue-tts",
voice="vh-es-alex",
input="Hello from VoiceHue.",
) as response:
response.stream_to_file("hello.mp3")import OpenAI from "openai";
import fs from "node:fs";
const client = new OpenAI({baseURL: "https://voicehue.com/v1", apiKey: process.env.VOICEHUE_API_KEY});
const audio = await client.audio.speech.create({
model: "voicehue-tts",
voice: "vh-es-alex",
input: "Hello from VoiceHue.",
});
fs.writeFileSync("hello.mp3", Buffer.from(await audio.arrayBuffer()));Load the MCP tools with langchain-mcp-adapters, or wrap the endpoint as your own tool.
import os
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({
"voicehue": {
"transport": "streamable_http",
"url": "https://voicehue.com/mcp",
"headers": {"Authorization": "Bearer " + os.environ["VOICEHUE_API_KEY"]},
}
})
tools = await client.get_tools() # text_to_speech, list_voicesimport os
from openai import OpenAI
from langchain_core.tools import tool
client = OpenAI(base_url="https://voicehue.com/v1", api_key=os.environ["VOICEHUE_API_KEY"])
@tool
def speak(text: str, voice: str = "VOICE_ID") -> str:
"""Convert text to speech with VoiceHue and save it as speech.mp3."""
with client.audio.speech.with_streaming_response.create(
model="voicehue-tts", voice=voice, input=text
) as response:
response.stream_to_file("speech.mp3")
return "speech.mp3"Use an HTTP request step with these settings, or import /openapi.json where the tool supports OpenAPI. Keep the key in the tool's credential store.
Method: POST
URL: https://voicehue.com/v1/audio/speech
Authentication: Header Auth
Name: Authorization
Value: Bearer vh_live_YOUR_KEY
Body (JSON): {"model": "voicehue-tts", "input": "{{your text}}", "voice": "VOICE_ID"}
Response: File / binary (the audio is the response body, MP3 by default)The three MCP calls an agent makes, for testing your key. The audio is in result.content, base64 encoded.
# 1. handshake (no key needed)
curl -s https://voicehue.com/mcp -H "Authorization: Bearer $VOICEHUE_API_KEY" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
# 2. list the tools
curl -s https://voicehue.com/mcp -H "Authorization: Bearer $VOICEHUE_API_KEY" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# 3. speak (the audio comes back base64 in result.content)
curl -s https://voicehue.com/mcp -H "Authorization: Bearer $VOICEHUE_API_KEY" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"text_to_speech","arguments":{"text":"Hello from VoiceHue.","voice":"VOICE_ID"}}}'The MCP server speaks the 2026-07-28 revision of the protocol and the earlier ones (2025-11-25 down to 2024-11-05), without sessions. A tool call that hits a limit returns the same error codes as the HTTP API (insufficient_quota, rate_limit_exceeded, invalid_api_key). One call takes up to 1,200 characters.
Send your key as a bearer token in the Authorization header of every request. Calls are meant for your server: the API does not allow browser (CORS) requests, so a key never has to appear in a web page.
Authorization: Bearer YOUR_API_KEY
POST https://voicehue.com/v1/audio/speech
Send a JSON body. The response body is the audio file, with Content-Type audio/mpeg (MP3, the default) or audio/wav when you send response_format: "wav". Fields other than those below are rejected with invalid_request, so typos do not go unnoticed. You can send an Idempotency-Key header to retry safely: a repeated key answers 409 duplicate_request. Each response carries X-VoiceHue-Request-Id; quote it when you contact support.
| Field | Type | Required | Description |
|---|---|---|---|
input | string | yes | The text to speak, up to 1,200 characters. |
voice | string | yes | A voice id from the list below. |
model | string | no | Accepted for compatibility and ignored. Use "voicehue-tts". |
response_format | string | no | mp3 (default) or wav. MP3 is returned whenever you leave it out or send mp3. A voice that cannot produce wav answers unsupported_format. |
speed | number | no | Speaking speed from 0.5 to 2. Default 1. |
Base URL for OpenAI clients: https://voicehue.com/v1. There are no rate-limit headers; the limits are listed below and a request over a limit answers 429.
Standard voices, the ones a free key can use (6)
voice | Name | Language | Type |
|---|---|---|---|
vh-es-alex | Alex | es-ES | female |
vh-fr-camille | Camille | fr-FR | female |
vh-en-sky | Sky | en-US | female |
vh-ko-haneul | 하늘 | ko-KR | female |
vh-ja-hikari | ひかり | ja-JP | female |
vh-zh-qingyin | 清音 | zh-CN | female |
Fetch this list in your code with GET https://voicehue.com/v1/voices. No key needed; add ?language=en to filter. Each id is what you send as voice.
Premium voices use your credits (1 credit = 1 premium character; some voices cost more). They need an API plan that includes premium voices. Premium voices are marked in the voice library.
Set model to voicehue-tts; it is accepted for compatibility with OpenAI clients and does not change the voice. Listen to the voices first.
| Keys per account | 2 |
|---|---|
| Characters per month, per key | 10,000 |
| Characters per day, per key | 2,000 |
| Requests per minute, per key | 10 |
| Characters in one request | 1,200 |
| Voices | Standard voices |
| Audio formats | MP3 (default), WAV |
Characters are counted when a request succeeds. The daily limit resets at 00:00 UTC and the monthly limit on the 1st of each month. The service also has a daily capacity for all keys together, so a request can be refused with 429 even when your own limits are not used up. There is no paid plan yet; paid credit packs are planned for later, without a date or a price.
Errors use the OpenAI shape: an error object with message, type and code. Messages are short and generic; branch on code, not on the text.
{
"error": {
"message": "The usage limit for this key has been reached.",
"type": "insufficient_quota",
"code": "insufficient_quota"
}
}| HTTP | type | code | When |
|---|---|---|---|
| 400 | invalid_request_error | invalid_request | The body is not valid JSON, has an unknown field, or a field has the wrong type. |
| 400 | invalid_request_error | invalid_input | The input contains characters that are not allowed (control characters). |
| 400 | invalid_request_error | invalid_voice | The voice value is malformed. |
| 400 | invalid_request_error | invalid_speed | speed is outside 0.5 to 2. |
| 400 | invalid_request_error | unsupported_format | The voice does not offer the requested response_format. |
| 401 | authentication_error | invalid_api_key | The key is missing, wrong, revoked or expired. The answer is identical in every case. |
| 429 | insufficient_quota | voice_daily_limit | This voice has a daily character limit and this key reached it. The error body carries limit and resets_at (00:00 UTC). |
| 402 | insufficient_quota | premium_trial_exhausted | A premium voice: today's free premium trial is used up and the account has no credits. The error body carries trial and credits. |
| 402 | insufficient_quota | insufficient_credits | A premium voice: the account does not have enough credits for this input. The error body carries trial and credits (balance and needed). |
| 403 | invalid_request_error | premium_not_allowed | A premium voice was requested but the plan of this key does not include premium voices. |
| 403 | invalid_request_error | premium_sign_in_required | A premium voice needs a signed-in account; the account behind the key is not one. |
| 403 | invalid_request_error | account_suspended | The account is not permitted to use the service. |
| 404 | invalid_request_error | voice_not_found | The voice does not exist or is not part of your plan. |
| 405 | invalid_request_error | invalid_request | The method is not POST. |
| 409 | invalid_request_error | duplicate_request | The same Idempotency-Key was already processed. |
| 413 | invalid_request_error | input_too_long | The input is longer than 1,200 characters. |
| 413 | invalid_request_error | audio_too_large | The generated audio is too large to convert to MP3. Send a shorter input. |
| 413 | invalid_request_error | request_too_large | The request body is larger than 16 KB. |
| 429 | rate_limit_error | rate_limit_exceeded | More than 10 requests in a minute for this key, or too many attempts from one address. Wait and retry. |
| 429 | insufficient_quota | insufficient_quota | A key, account-wide, or shared-network free allowance has been used up. |
| 502 | server_error | service_unavailable | The service could not complete the request. Retry later. |
| 503 | server_error | service_unavailable | The API is switched off or temporarily unavailable. |
The request and error format follow the OpenAI speech endpoint. With an official OpenAI client, set the base URL to https://voicehue.com/v1 and the API key to your VoiceHue key, then call audio.speech.create as in the quickstart. VoiceHue is its own service and is not affiliated with OpenAI.
No. The API only follows the same request and error format, so the official OpenAI clients work if you set base_url to the VoiceHue address and use a VoiceHue key. Plain HTTP works too.
The request is refused with HTTP 429 and an error code (insufficient_quota for the daily or monthly characters, rate_limit_exceeded for requests per minute). Nothing is charged. The daily limit resets at 00:00 UTC and the monthly limit on the 1st of the month.
The service keeps counts for limits, not the text or the audio you send. See the privacy page for details.
Paid credit packs are planned for later. There is no date and no price yet.
Tell us what you tried and what came back, and we will help you get it working.