Cắm thẳng vào app tương thích OpenAI
Rất nhiều phần mềm đã nói được POST /v1/audio/speech và cho phép bạn trỏ nó tới
một base URL riêng. VieNeu có sẵn endpoint đó, nên những app này dùng được giọng
tiếng Việt mà không cần plugin, không cần adapter.
Trang này là bảng khai báo: với mỗi client, điền giá trị nào vào ô nào.
Phần VieNeu trong mọi mục dưới đây là chính xác. Phần client được viết theo kiểu "giá trị này thuộc về loại ô nào", vì nhãn của các ô cài đặt thay đổi theo phiên bản — hãy đối chiếu tên trường với đúng bản bạn đang chạy.
Ba giá trị cần có
Client nào cũng cần đúng ba thứ giống nhau.
| Giá trị | Điền gì |
|---|---|
| Base URL | https://api.vieneu.io/api/v1 (xem cái bẫy bên dưới) |
| API key | key VieNeu của bạn — bắt đầu bằng vn_sk_ (thật) hoặc vn_test_ (test) |
| Model | tts-1, tts-1-hd, gpt-4o-mini-tts, vieneu hoặc vieneu-v4 → v4, engine duy nhất kể từ khi v3 ngừng ngày 2026-09-24. Mọi tên khác đều được nhận rồi bỏ qua — trừ một tên vieneu-… không trỏ tới engine đang chạy, cái đó là 400. vieneu-v3 giờ là một trong số đó. |
Ngoại lệ duy nhất ở dòng cuối lại chính là chỗ người dùng VieNeu dễ vấp nhất.
vieneu-v5 hay vieneu-turbo không rơi về engine mặc định theo kiểu
whatever-tts; nó trả về 400 invalid_request_error kèm param: "model", liệt
kê những tên engine thực sự tồn tại. Tiền tố đó được đọc như một lựa chọn engine
tường minh, mà âm thầm đọc nó bằng engine khác thì bạn bị tính phí theo mức bạn
chưa từng chọn. Nếu bạn gửi cả engine lẫn model, engine thắng.
vieneu-v3 mới là trường hợp bạn dễ gặp thật nhất. Nó là lựa chọn hợp lệ cho
tới khi v3 rút khỏi Cloud API ngày 2026-09-24, và giờ trả về 400 kèm
Model 'vieneu-v3' is retired. Engine "v3" was retired on 2026-09-24. Use engine "v4" and a voice from GET /v1/voices?engine=v4 — the two catalogues share no ids. Hãy đổi model sang vieneu-v4 (hoặc tts-1) và kiểm lại giọng trong cùng
một lượt — một slug vieneu-… lưu từ danh mục v3 cũ sẽ 400 ở param: "voice"
ngay sau đó.
Trường thứ tư, voice, không bắt buộc nhưng thường thì bạn vẫn muốn có: bỏ trống
thì server dùng giọng đang bật đầu tiên trên engine được chọn. Thứ bạn không làm
được là dùng lại tên của OpenAI — alloy / nova v.v. không được ánh xạ và
trả về 400. Lấy id thật từ GET /api/v1/audio/voices (bên dưới).
Cái bẫy base URL
Path là /api/v1/audio/speech. Cái /api/v1 trông như bị lặp đó là thật —
server đặt một prefix api toàn cục và gắn API công khai ở v1. Nên giá trị
bạn gõ vào phụ thuộc vào phần client tự nối thêm:
| Nếu ô đó có nghĩa là… | Điền |
|---|---|
"base URL của OpenAI — tôi tự nối /audio/speech" | https://api.vieneu.io/api/v1 |
"host — tôi tự nối /v1/audio/speech" | https://api.vieneu.io/api |
| "URL endpoint đầy đủ" | https://api.vieneu.io/api/v1/audio/speech |
Phần lớn client hiểu theo dòng đầu. Chọn sai thì luôn là 404, và luôn là JSON — nhưng theo cấu trúc lỗi nền tảng của VieNeu, chứ không phải cái phong bì OpenAI mà endpoint này vẫn dùng ở mọi chỗ khác:
{ "statusCode": 404, "message": "Cannot POST /api/v1/v1/audio/speech", "error": "Not Found" }
Đọc được hai điều từ đó:
- Thông báo
Cannot POST …và việc vắng mặt lớp bọc{"error":{"message","type","param","code"}}mà mọi lỗi thật của/v1/audio/speechđều mang, có nghĩa là hình dạng URL sai chứ không phải key sai. Đừng đi lục API key vì lỗi này. - Path nằm trong thông báo chính là URL client của bạn thực sự dựng ra. So nó với
/api/v1/audio/speech, chỗ lệch sẽ cho biết bạn cần dòng nào ở bảng trên — ví dụ ở đây bị lặp/v1, nên client đó cầnhttps://api.vieneu.io/api.
Không có /v1/models
VieNeu chỉ phục vụ TTS. Không có route /v1/models, cũng không có
/v1/chat/completions. Hai hệ quả:
- Nút "Test connection" / "Verify" nào dò
/modelssẽ báo thất bại, dù việc tổng hợp giọng vẫn chạy. Cứ kệ nó và gửi một request thật. - Ô chọn model lấy dữ liệu từ
/modelssẽ trống rỗng. Hãy gõ tay tên model.
Trong Open WebUI, SillyTavern và LobeChat, base URL này chỉ thuộc về phần cài đặt provider audio/TTS — đừng bao giờ đặt vào ô endpoint OpenAI/LLM chung, đặt vào đó là hỏng model chat.
Chứng minh nó chạy trước đã
Trước khi đụng vào bất kỳ client nào, hãy xác nhận base URL và key bằng curl. Gửi
$VIENEU_API_KEY — key vn_sk_… hoặc vn_test_… của chính bạn — và không gửi
voice, để ngoài hai giá trị đó ra không còn gì có thể hỏng:
curl https://api.vieneu.io/api/v1/audio/speech \
-H "Authorization: Bearer $VIENEU_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "tts-1", "input": "Xin chào, đây là VieNeu." }' \
--output speech.mp3
Bỏ voice đi thì server tự chọn giọng đang bật đầu tiên trên engine được chọn,
và đó là thứ giữ cho bước này trung thực: một voice id bạn chưa kiểm chứng sẽ 400
ở param: "voice" và bạn hết đường phân biệt giọng sai với key sai hay base URL
sai. Chọn một giọng thật ở bước sau, khi bước này đã qua.
Nếu lệnh đó cho ra audio nghe được thì mọi thứ sau đây chỉ là chuyện cấu hình client.
Lấy voice id
# Which engine will my requests land on? No API key needed.
curl https://api.vieneu.io/api/v1/engines
# Voice ids for that engine. API key IS required here.
curl "https://api.vieneu.io/api/v1/audio/voices?engine=v4" \
-H "Authorization: Bearer $VIENEU_API_KEY"
GET /api/v1/engines trả về mỗi engine đang bật một mục, kèm key, isDefault,
billingMultiplier và features. Hãy dùng cái có isDefault: true — từ khi
v3 ngừng ngày 2026-09-24 thì đó là mục duy nhất, v4.
audio/voices giờ chỉ liệt kê v4, có lọc hay không cũng vậy, và ?engine=v3
là 400. Nhưng id của danh mục V3 đã ngừng là slug vieneu-…, id của V4 là tên
hiển thị (Ngọc Lan), và hai bên gần như không chung không gian id nào — nên
một giọng mà client lưu từ danh sách chưa lọc trước ngày ngừng sẽ 400 ở
param: "voice" bây giờ. Hãy nạp lại ô chọn giọng từ lời gọi ở trên, và cứ
truyền ?engine=v4: không tốn gì mà request lại tường minh.
Việc khớp giọng không phân biệt hoa thường, nhưng có phân biệt dấu, và id của V4 có dấu cách. Client nào slugify hoặc bỏ dấu trước khi gửi sẽ 400 ở mọi request.
Giờ chạy lại phép thử nhanh với một id lấy từ danh sách đó, chép đúng y như lúc nhận về:
curl https://api.vieneu.io/api/v1/audio/speech \
-H "Authorization: Bearer $VIENEU_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "tts-1", "input": "Xin chào, đây là VieNeu.", "voice": "PASTE_AN_ID_HERE" }' \
--output speech.mp3
Nếu lệnh curl đầu chạy được mà lệnh này 400 ở param: "voice" thì id là thứ duy
nhất đã thay đổi — hãy kiểm bộ lọc engine và dấu tiếng Việt trước đã.
Id clone_… bị /v1/audio/speech từ chối, kèm thông báo nêu tên hai route nhận
chúng: POST /v1/tts và POST /v1/tts/stream. Không client tương thích OpenAI
nào chạm tới được giọng nhân bản.
Open WebUI
Phần cài đặt Audio/TTS, engine OpenAI:
| Trường | Giá trị |
|---|---|
| TTS engine / provider | tuỳ chọn tương thích OpenAI |
| API base URL | https://api.vieneu.io/api/v1 |
| API key | vn_sk_… |
| Model | tts-1 |
| Voice | một id thật từ audio/voices, ví dụ Ngọc Lan |
Ghi chú:
- Đặt cái này vào phần cài đặt audio, không phải phần model/connection.
- Ô voice phải nhận được chữ nhập tự do. Nếu bản bạn chạy chỉ đưa ra sáu cái tên của OpenAI thì cả sáu đều 400 — hãy kiểm trên bản của bạn.
- Response format: cứ để mặc định. Open WebUI phát mp3, cũng đúng là mặc định của VieNeu.
SillyTavern
Extension TTS, provider tương thích OpenAI:
| Trường | Giá trị |
|---|---|
| Provider | tuỳ chọn OpenAI TTS |
| API / base URL | https://api.vieneu.io/api/v1 |
| API key | vn_sk_… |
| Model | tts-1 |
| Voice (theo từng nhân vật) | một id thật từ audio/voices |
Ghi chú:
- SillyTavern gán giọng cho từng nhân vật. Mọi nhân vật đều phải mang id VieNeu —
một nhân vật còn để
alloysẽ hỏng trong khi số còn lại chạy, nhìn vào cứ tưởng tích hợp lúc được lúc không. - Nếu extension chỉ cho một ô chọn giọng cố định thay vì ô nhập chữ thì nó không gọi được giọng VieNeu. Hãy kiểm trên bản của bạn.
- Nếu SillyTavern của bạn gọi TTS từ trình duyệt chứ không từ server của chính nó, xem Client chạy phía trình duyệt.
LobeChat
Phần cài đặt TTS / audio, provider OpenAI:
| Trường | Giá trị |
|---|---|
| OpenAI TTS base URL / proxy URL | https://api.vieneu.io/api/v1 |
| API key | vn_sk_… |
| Model | tts-1 |
| Voice | một id thật từ audio/voices |
Ghi chú:
- LobeChat giữ hai phần cài đặt riêng cho provider chat và provider TTS. URL này chỉ đặt vào phần TTS.
- LobeChat thường được triển khai theo kiểu trình duyệt gọi thẳng tới provider TTS — hãy xem Client chạy phía trình duyệt trước khi gỡ bất cứ thứ gì khác.
LiteLLM
Thêm VieNeu như một model trong config.yaml:
model_list:
- model_name: vieneu-tts
litellm_params:
model: openai/tts-1
api_base: https://api.vieneu.io/api/v1
api_key: os.environ/VIENEU_API_KEY
Rồi khi proxy đang chạy, gọi nó y hệt như gọi OpenAI:
curl http://localhost:4000/v1/audio/speech \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "vieneu-tts", "input": "Xin chào", "voice": "Ngọc Lan" }' \
--output speech.mp3
Ghi chú:
LITELLM_MASTER_KEYkhông phải key VieNeu của bạn. Đó là credential proxy của riêng LiteLLM, là bất cứ thứ gì bạn đặt lúc khởi động proxy — cổng (:4000ở trên) cũng là của LiteLLM. Keyvn_sk_…của bạn chỉ xuất hiện trongconfig.yaml, lấy quaos.environ/VIENEU_API_KEY; chính proxy mới là bên gắn nó vào lời gọi lên upstream.- Tiền tố
openai/trongmodelbảo LiteLLM chuyển tiếp request theo hình dạng của OpenAI, đúng thứ VieNeu trả lời được. Cái tên đứng sau nó (tts-1) mới là thứ đi tới VieNeu. - Các giá trị VieNeu ở trên là chính xác; những key LiteLLM bao quanh chúng là
hình dạng
model_listchuẩn của nó — hãy đối chiếu với phiên bản LiteLLM bạn dùng. - Đừng bật bất kỳ tính năng làm giàu request nào có thêm field vào body. VieNeu từ chối field lạ bằng 400 chứ không bỏ qua chúng — xem 400.
LiveKit Agents
Plugin OpenAI nhận một base URL và một key:
from livekit.plugins import openai
tts = openai.TTS(
model="vieneu-v4",
voice="Ngọc Lan",
base_url="https://api.vieneu.io/api/v1",
api_key="vn_sk_...",
)
Ghi chú:
- Hãy ghim
vieneu-v4, hoặc bỏ trốngmodel. Cả hai đều rơi vàov4, engine duy nhất kể từ khiv3ngừng ngày 2026-09-24.vieneu-v3— thứ mà endpoint này từng nhận kèm streaming, trả 200 rồi để v3 nhả hết chunk ra một lượt ở cuối mà không có lỗi nào báo cho bạn biết — giờ là 400 kèmparam: "model"nói rằng engine đã ngừng. GET /v1/enginestrả về hệ số tính phí đang chạy; xem Engine.- Muốn streaming, VieNeu cần cả hai:
response_format: "pcm"vàstream_format.response_formatmặc định là mp3, nên chỉ đặt mỗistream_formatlà 400. Plugin có gửi cái nào trong hai cái đó không, và có cho bạn thêm vào không, chính là thứ cần kiểm trên bản bạn chạy. Thiếu chúng thì lời gọi vẫn chạy — chỉ là nó trả về một file mp3 hoàn chỉnh thay vì một luồng. pcmkhông có header và mặc định 24000 Hz trên route này (tần số gốc của engine là 48000). Hãy đọc tần số thật từ header phản hồiX-Sample-Raterồi cấu hình pipeline cho khớp, không thì audio phát sai cao độ.
Pipecat
import os
from pipecat.services.openai.tts import OpenAITTSService
tts = OpenAITTSService(
api_key=os.environ["VIENEU_API_KEY"],
base_url="https://api.vieneu.io/api/v1",
model="vieneu-v4",
voice="Ngọc Lan",
)
Ghi chú:
- Đường dẫn import cho service OpenAI TTS của Pipecat đã đổi chỗ qua các bản phát hành — hãy kiểm bản của bạn. Thứ quan trọng là các tham số constructor và những giá trị ở trên.
- Ba quy tắc streaming giống hệt LiveKit vẫn áp dụng: ghim
vieneu-v4, gửiresponse_format: "pcm"vàstream_format, và lấy tần số từX-Sample-Ratethay vì mặc định coi là 48 kHz. - Nếu phiên bản của bạn hard-code một
response_formatmà VieNeu không nhận (aac,flac) thì request 400 kèm tên field đó. Các giá trị được nhận làwav,mp3,opus,pcmvàulaw.
Client chạy phía trình duyệt
Trên production, chính sách CORS của VieNeu chỉ cho phép các origin của chính
VieNeu. Client nào gọi /v1/audio/speech từ trang web chứ không từ server
của chính nó sẽ chết ngay ở bước preflight, không có body phản hồi nào giải thích.
Một bản triển khai LobeChat hay SillyTavern cụ thể có gọi TTS ở phía server hay không là chuyện tuỳ từng bản — hãy kiểm trên bản của bạn. Nếu nó chạy phía trình duyệt, hãy dựng một proxy của riêng bạn đứng trước (LiteLLM hợp cho việc này) rồi trỏ client vào đó.
JavaScript trong trình duyệt đọc được X-Request-Id, X-Sample-Rate,
X-Output-Format, các header X-RateLimit-* và Retry-After; mọi thứ còn lại
bị trình duyệt che. (X-Stream-Format cũng được mở qua CORS, nhưng đừng viết code
đọc nó ở đây — chỉ route gốc POST /v1/tts/stream mới gửi nó. Trên route này
phần đóng khung đã được bóc sẵn cho bạn rồi, nên header đó luôn vắng mặt.)
Gỡ rối theo triệu chứng
Không có audio
Đi lần lượt xuống danh sách — mỗi mục một nguyên nhân khác nhau.
- Tệp lưu về chứa JSON. Trên đường stream thô, một lượt sinh không ra được gì
sẽ trả 502 kèm body lỗi ở đúng chỗ lẽ ra là audio. Client nào ghi thẳng
body phản hồi vào tệp
.pcmsẽ có JSON nằm trong đó. Hãy xem những byte đầu tiên của tệp. - Tệp bị tải về thay vì phát. Phản hồi đồng bộ mang
Content-Disposition: attachment. Client nào fetch phần body thì không bị ảnh hưởng; client nào điều hướng thẳng tới URL sẽ nhận một lượt tải về. - Audio phát sai cao độ hoặc sai tốc độ.
pcmvàulawkhông có header — chuỗi byte không mang theo sample rate. Ở đâypcmlà 24000 Hz trừ khi bạn yêu cầu khác;ulawluôn là 8000. Hãy đọcX-Sample-Ratethay vì đoán. - Trình phát từ chối một tệp
.mp3. Nếu bạn bỏ trốngresponse_formatmà worker đang phục vụ bạn không mã hoá được mp3, VieNeu lùi về byte WAV thay vì báo lỗi. Hãy đọcX-Output-Formatđể biết bạn thực sự nhận được gì. (Nếu bạn đã yêu cầu mp3 tường minh thì thay vào đó bạn sẽ nhận 503 kèm tên định dạng — một lựa chọn tường minh không bao giờ bị âm thầm thay thế.)
400 invalid_request_error
Với hầu hết các trường hợp này, field param trong body lỗi nêu đích danh thủ
phạm — trừ đúng một ngoại lệ, và đó là nguyên nhân đầu tiên trong danh sách dưới
đây. Các nguyên nhân thường gặp:
-
Một field lạ trong body. VieNeu từ chối những field nó không khai báo chứ không bỏ qua chúng. Bộ field được nhận đúng bằng:
model,input,voice,response_format,sample_rate,speed,instructions,stream_format,emotion,aiRefine,engine. Lưu ý là không có booleanstream— client nào gửistream: true(như với chat completions) sẽ nhận 400.user,languagehay bất kỳ phần mở rộng riêng nào của client cũng vậy. Đây là lý do phổ biến nhất khiến một client chạy tốt với OpenAI lại hỏng với VieNeu: hãy soi đúng cái body mà nó thực sự gửi đi.Với trường hợp này, hãy đọc
messagechứ không phảiparam. Lỗi bị chặn ở bộ validator của request chứ không ở handler, nênparamtrả về đúng chuỗi"property"chứ không bao giờ là tên field vi phạm. Tên nằm trong message:property stream should not exist. Mỗi request chỉ báo field vi phạm đầu tiên, nên nếu client của bạn thêm mấy field một lúc thì cứ sửa rồi thử lại cho tới khi qua. -
Một tên model
vieneu-…không nhận ra.param: "model". Các tên model lạ khác thì bị bỏ qua; riêng tiền tố này thì không. -
Một tên giọng của OpenAI.
alloy,echo,fable,onyx,nova,shimmerkhông được ánh xạ.param: "voice". -
Một voice id nhân bản.
clone_…chỉ chạy trênPOST /v1/ttsvàPOST /v1/tts/stream. -
stream_formatđi với một định dạng không stream được. Chỉpcmvàulawstream được, màresponse_formatlại mặc định là mp3 — nên chỉ đặt mỗistream_formatthì luôn luôn 400. -
aachoặcflac. Định dạng có thật của OpenAI nhưng VieNeu không mã hoá được; thông báo nói thẳng ra điều đó. -
Một
sample_ratemâu thuẫn với định dạng.opusluôn là 48000 vàulawluôn là 8000; một tần số xung đột sẽ bị từ chối chứ không bị ghi đè. Các tần số hợp lệ là 8000, 16000, 22050, 24000, 44100, 48000. -
speednằm ngoài 0.25–4.0. Nằm trong dải đó thì nó bị kẹp về 0.5–2.0 và không bao giờ báo lỗi; nằm ngoài thì bộ kiểm tra từ chối. -
Key test vượt quá 100 từ. Key
vn_test_chặn mỗi request ở 100 từ tách bằng khoảng trắng. Thông báo nêu đúng số từ thực tế của bạn.
401 authentication_error
Invalid API key format.— key phải bắt đầu đúng bằngvn_sk_hoặcvn_test_. Chỗ này được kiểm trước mọi thao tác tra cứu, nên những key giữ chỗ mà vài client kèm sẵn hoặc bắt phải điền (sk-...,none,ollama) đều chết ở đây. Nếu client không chịu lưu khi ô key để trống thì nó cần một key VieNeu thật.API key required.dù bạn đã điền — một bearer token có chứa dấu chấm sẽ bị coi là JWT và bị bỏ qua với tư cách API key. Key VieNeu thật là hex và không bao giờ có dấu chấm, nên lỗi này nghĩa là có thứ gì đó đã bọc hoặc thay mất key của bạn.Invalid or revoked API key.— một key đã thu hồi thì không phân biệt được với một key lạ.
Key đặt ở Authorization: Bearer <key> hoặc X-API-Key: <key>; cả hai đều được.
Nếu client của bạn gửi cả hai với giá trị khác nhau thì X-API-Key âm thầm thắng.
403 — thường là "hết token"
Ở đây hết token là 403, không phải 402. Không chỗ nào trong API công khai
phát ra 402, nên một client chỉ canh mã đó sẽ chẳng bao giờ biết mình đã hết
tiền. Hãy canh 403. Có ba thông báo, đều mang type insufficient_quota:
Insufficient tokens. You have <n> tokens remaining but this request costs <m> tokens.— đây là lúc một grant hết theo cách bình thường, và là lỗi đầu tiên bạn gặp khi hạn mức dùng thử cạn.Your API token package has expired. Please renew your Developer plan.— chuyện thời hạn, không phải chuyện tiêu thụ. Mua thêm token không chữa được; gia hạn mới chữa được.API token grant is not active. Check your Developer plan status.
Hai lỗi 403 khác thì không liên quan tới token, và field type là thứ phân
biệt chúng:
Your plan does not include engine "v4". Upgrade your Developer plan to use it.— mang typeinvalid_request_error,param: "engine". Chỉ phát sinh khi bạn ghim engine tường minh mà gói không bao gồm nó; vớiv4là engine duy nhất từ 2026-09-24 thì hiếm khi bạn gặp — bỏmodel/engineđi thì engine mặc định chạy được.This feature is not available yet.— mang typeauthentication_error, đến từ một cổng tính năng chứ không phải từ khâu tính phí.
Vậy nên đừng rẽ nhánh chỉ theo type để nhận ra "hết token", mà cũng đừng rẽ
nhánh chỉ theo 403. Hai thứ đi cùng nhau thì không còn nhập nhằng.
Trần theo ngày hoặc theo tuần là 429, không phải 403 — xem bên dưới.
429 rate_limit_exceeded
Đọc header để phân biệt nguồn gốc:
| Header có mặt | Nguồn | Đếm vào |
|---|---|---|
Retry-After + X-RateLimit-* | bộ throttle của VieNeu trên các route tổng hợp giọng — 300 request/phút theo mặc định | API key của bạn |
| không có cái nào, body không phải JSON | proxy ở biên — 3000 request/phút | IP nguồn của bạn, dùng chung với mọi người đứng sau nó |
JSON, message có chứa Resets at <ISO> | trần ngày hoặc trần tuần của token grant | grant của bạn |
Cả ba trường hợp đều phải giãn nhịp. Trường hợp giữa đáng để biết: nếu client của bạn đi ra qua một đường egress dùng chung hoặc có NAT, bạn có thể chạm phải một giới hạn mà chỉnh bao nhiêu ở phía mình cũng không chữa được.
Đừng hard-code số 300. Đó là một cài đặt theo bản triển khai
(PUBLIC_API_SYNTHESIS_RPM), không phải hằng số, và các trang khác nêu con số
khác vì chúng được viết dựa trên những bản triển khai khác. Phần bền vững là cột
đầu của dòng đó: hãy đọc hạn mức đang chạy của bạn từ X-RateLimit-Limit và
X-RateLimit-Remaining trên bất kỳ phản hồi thành công nào.
Dòng thứ ba là dòng người ta hay nhầm thành dòng đầu. Trần ngày hoặc trần tuần
của grant là một lỗi 429 mà message kết thúc bằng Resets at <ISO timestamp> —
chậm lại cỡ nào cũng không gỡ được nó trước thời điểm đó. Còn hết sạch token thì
là 403, không bao giờ là 402.
503 — lỗi ở đội máy, không phải ở request của bạn
Không có gì trong request của bạn cần sửa; cứ thử lại sau chốc lát. Bốn nguyên nhân:
- Không có worker nào cho engine bạn đã ghim.
No V4 TTS worker is available right now. Please try again shortly.(đường streaming thì nói…available for streaming right now.) Chỉ phát sinh với engine không phải mặc định: một request đi theo engine mặc định sẽ lùi về worker đã cấu hình chứ không trả 503, nên bỏmodel/enginelà cách chữa cháy hợp lệ ở đây. - Không có giọng nào đang bật trên engine đó, khi bạn bỏ trống
voice.No voice is currently available on engine "v4". Pass an explicit voiceId from GET /v1/voices.Lỗi này về dưới dạng 503 nhưng mang typeinvalid_request_errorkèmparam: "voice"— một vấn đề của danh mục giọng lại đeo cái nhãn của lỗi request. Nó phát sinh trước khâu tính phí, nên bạn chưa bị trừ gì. - Một định dạng bạn yêu cầu tường minh mà worker không mã hoá được. Nếu bạn
bỏ trống
response_formatthì thay vào đó bạn đã nhận được byte WAV; một lựa chọn tường minh không bao giờ bị âm thầm thay thế. Xem Không có audio. - Một
sample_rateworker không tạo ra được, trên đường streaming. Thông báo nêu cả thứ bạn yêu cầu lẫn thứ worker đưa ra được.
Mọi trường hợp trong số này mà đã đi tới khâu tính phí đều được hoàn tự động — bạn không bị tính tiền cho audio bạn không nhận được.
Luồng dừng giữa chừng câu
- Với SSE, hãy đợi sự kiện kết thúc. Không có dấu hiệu
data: [DONE]nào cả. Luôn luôn có đúng mộtspeech.audio.done(audio trọn vẹn, mang theousage) hoặcspeech.audio.error(thì không trọn vẹn) đi tới. Nếu chẳng cái nào tới thì luồng đã bị cắt cụt — hãy bỏ đoạn audio đi; request được hoàn tự động. - Trên đường stream thô thì không có tín hiệu như vậy. Một luồng bị cắt cụt
không tài nào phân biệt được với một luồng vốn dĩ ngắn. Nếu bạn cần bằng chứng
về sự trọn vẹn, hãy dùng
stream_format: "sse"hoặc route gốcPOST /v1/tts/stream. - Nó "treo", rồi đổ ra tất cả một lượt. Trước đây đó là
vieneu-v3đi kèmstream_format: endpoint này nhận, trả 200, rồi v3 nhả hết chunk ra một lượt ở cuối. Từ khiv3ngừng ngày 2026-09-24, request đó là 400, nên nếu bạn vẫn thấy kiểu này thì việc đệm đang xảy ra ở một proxy hay client của chính bạn, không phải ở engine. Hãy ghimvieneu-v4và kiểm xem có gì nằm giữa bạn và API. - Một lời gọi không streaming bị timeout với text dài. Trên endpoint này
inputkhông có trần độ dài — request chỉ đơn giản là chặn đó suốt trọn thời gian tổng hợp, rồi cái timeout HTTP của chính client bỏ cuộc trước. Trần thực sự là body request 2 MB và read timeout 600 giây ở proxy. Với text dài, hãy dùng đường bất đồng bộPOST /api/v1/tts+GET /api/v1/tts/{jobId}thay cho nó.
Mọi phản hồi đều mang X-Request-Id. Hãy dẫn nó ra khi gửi yêu cầu hỗ trợ.
Xem thêm
- Tổng quan tích hợp — chọn đường nào nếu công cụ của bạn không có trong trang này.
- Endpoint TTS tương thích OpenAI — hợp đồng
đầy đủ theo từng field cho
/v1/audio/speech: mọi field được nhận, quy tắc về định dạng và sample rate, cùng danh sách lỗi trọn vẹn. - Streaming — so sánh hai endpoint streaming, và ngữ nghĩa của sự kiện kết thúc đã tóm ở trên.
- Hạn mức và request id — các header, và vì sao hạn mức đếm theo từng key.
- API reference — sinh từ chính đặc tả OpenAPI của server.