ถอดเสียงบันทึกเป็นข้อความ
ปลายทางถอดเสียงแปลงไฟล์บันทึกเสียงภาษาไทยหรืออังกฤษเป็นบทถอดตามที่พูดจริง พร้อมเวลาระดับคำเมื่อร้องขอ คิดเครดิตตามความยาวของเสียง ตัดเครดิตก่อนถอดและคืนให้อัตโนมัติเมื่อไม่สำเร็จ
POST /v1/stt อ่านไฟล์บันทึกเสียงแล้วคืนข้อความที่พูด ส่งไฟล์เป็น base64 ในฟิลด์ audio พร้อมรหัสโมเดล paxa-stt-lite-v1-preview บทถอดคืนมาตามที่พูดจริงในภาษาที่พูด ประโยคภาษาไทยที่สลับเป็นอังกฤษกลางทางจะถูกเขียนตามที่พูด ไม่มีการแปล เช่นเดียวกับทุกปลายทาง ระบบตัดเครดิตก่อนประมวลผลและคืนให้อัตโนมัติเมื่อประมวลผลไม่สำเร็จ (เครดิต)
หมายเหตุ
paxa-stt-lite-v1-preview เป็นพรีวิวงานวิจัย คำขอคิดเครดิตตามอัตราที่ประกาศ คุณภาพบทถอดและชุดตัวเลือกอาจเปลี่ยนแปลงได้ในรุ่นถัดไป โดยบันทึกการเปลี่ยนแปลงในหน้าผลิตภัณฑ์ จะระบุทุกครั้ง
# Encode without line wrapping: wrapped base64 breaks the JSON string.
AUDIO=$(base64 < meeting.m4a | tr -d '\n')
# --max-time covers an hour-long recording; curl defaults to no limit.
curl -X POST https://api.paxalabs.com/v1/stt \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"audio\": \"$AUDIO\", \"model\": \"paxa-stt-lite-v1-preview\"}"import { readFile } from "node:fs/promises";
const audio = (await readFile("meeting.m4a")).toString("base64");
const response = await fetch("https://api.paxalabs.com/v1/stt", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAXA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ audio, model: "paxa-stt-lite-v1-preview" }),
// A long recording can run for minutes; give it room.
signal: AbortSignal.timeout(300_000),
});
if (!response.ok) throw new Error(`Transcription failed: ${response.status}`);
const { text, usage } = await response.json();
console.log(text, `${usage.credits} credits`);import base64
import os
import requests
with open("meeting.m4a", "rb") as file:
audio = base64.b64encode(file.read()).decode()
response = requests.post(
"https://api.paxalabs.com/v1/stt",
headers={"Authorization": f"Bearer {os.environ['PAXA_API_KEY']}"},
json={"audio": audio, "model": "paxa-stt-lite-v1-preview"},
# A long recording can run for minutes; give it room.
timeout=300,
)
response.raise_for_status()
body = response.json()
print(body["text"], body["usage"]["credits"], "credits"){
"text": "สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน พันสองร้อยห้าสิบ บาทครบกำหนดวันที่ สิบห้า สิงหาคมนี้ชำระผ่านแอปได้ตลอด ยี่สิบสี่ ชั่วโมงค่ะ",
"usage": {
"seconds": 10.25,
"credits": 1.43
}
}การคิดเครดิต#
ไฟล์เสียงคิด 8.33 เครดิตต่อนาทีของเสียง หรือ 500 เครดิตต่อชั่วโมง ตามความยาวจริงของเสียง ระบบอ่านความยาวจากตัวไฟล์ก่อนตัดเครดิต จึงรู้ค่าใช้จ่ายก่อนเริ่มฟังเสมอ ทุกคำขอคิดขั้นต่ำ 0.1 เครดิต เวลาระดับคำ ช่วงผู้พูด สไตล์ คำใบ้ภาษา รูปแบบการเขียน และคลังคำศัพท์ไม่คิดเพิ่ม
ยอดที่ตัดรายงานอยู่ใน usage.credits ของเนื้อหาคำตอบเท่านั้น คู่กับ usage.seconds คือความยาวที่คิดเครดิต ปลายทางนี้ไม่ส่งเฮดเดอร์ x-credits-charged คำตอบที่เร็วและช้าจึงมีรูปแบบเดียวกัน เมื่อถอดเสียงไม่สำเร็จหลังตัดเครดิต ระบบคืนเครดิตอัตโนมัติและตอบ 502 provider_error ไฟล์ที่ไม่มีเสียงพูดเลยจะได้บทถอดว่างเปล่าเป็นผลลัพธ์ที่ส่งมอบแล้ว และคิดเครดิตตามความยาว
ไฟล์เสียงและรูปแบบ#
ฟิลด์ audio คือไบต์ของไฟล์ในรูป base64 ระบบอ่านเจ็ดรูปแบบ ได้แก่ MP3, WAV (PCM), FLAC, Ogg (Opus หรือ Vorbis), M4A, AAC (ADTS) และ WebM โดยตรวจรูปแบบและความยาวจากไบต์ของไฟล์เอง ไม่เคยเชื่อฟิลด์ที่ประกาศมา ไฟล์ที่อ่านเป็นเจ็ดรูปแบบนี้ไม่ได้จะตอบ 400 audio_invalid เช่นเดียวกับไฟล์ที่อ่านความยาวจากคอนเทนเนอร์ไม่ได้ เช่น FLAC ที่เขียนโดยไม่มีจำนวนตัวอย่างเสียง ทุกกรณีข้างต้นไม่ตัดเครดิต
base64 ทำให้ขนาดโตขึ้นราวหนึ่งในสาม และไฟล์ที่บีบอัดแล้วเดินทางเร็วกว่า Opus ที่ 24 kbps หรือ MP3 ที่ 64 kbps บรรจุเสียงพูดหนึ่งชั่วโมงได้ภายในขีดจำกัดขนาด ไฟล์ที่บันทึกในเบราว์เซอร์ด้วย MediaRecorder เป็น WebM ซึ่งระบบอ่านความยาวจากบล็อกของไฟล์ได้ จึงไม่ต้องแปลงก่อนส่ง
ขีดจำกัดความยาวและขนาด#
หนึ่งคำขอถอดเสียงได้ไม่เกิน 60 นาที ไฟล์ที่ยาวกว่านั้นตอบ 400 audio_too_long ไฟล์หลังถอดรหัส base64 ต้องไม่เกิน 25 MiB ไฟล์ที่ใหญ่กว่าตอบ 413 audio_too_large การปฏิเสธทั้งสองแบบไม่ตัดเครดิต ไฟล์ที่ยาวกว่านี้ให้ตัดตรงช่วงเงียบแล้วส่งเป็นหลายคำขอ
การแยกผู้พูด มีขีดจำกัดของตัวเองที่สั้นกว่านั้น ไฟล์ยาวตั้งแต่ 9 นาทีขึ้นไปที่ตั้ง diarization เป็น true จะตอบ 400 diarization_audio_too_long โดยไม่ตัดเครดิต ไฟล์เดียวกันถอดเสียงได้ตามปกติเมื่อปิดฟิลด์นี้
ภาษา#
ฟิลด์ language บอกโมเดลว่าควรคาดหวังภาษาใด เป็นแท็ก BCP 47 เช่น "th" หรือ "en" ค่านี้เป็นคำใบ้และไม่กรองสิ่งใด เสียงพูดภาษาอื่นยังถูกถอดตามที่พูด ไฟล์ภาษาไทยที่มีคำและวลีอังกฤษปนอยู่จึงไม่ต้องตั้งค่าใด หากไม่ระบุ โมเดลตรวจจับภาษาเอง ระบุแท็กเมื่อการตรวจจับเลือกภาษาผิดสำหรับไฟล์นั้น
เวลาระดับคำ#
ตั้ง timestamps เป็น "word" แล้วคำตอบจะมีอาร์เรย์ words หนึ่งช่วงต่อหนึ่งคำที่รู้จำได้ พร้อมข้อความและเวลาเริ่มกับสิ้นสุดเป็นวินาทีบนนาฬิกาของไฟล์เอง ระบบตัดคำภาษาไทยให้ ส่วนคำละตินและตัวเลขมาทั้งคำ เป็นโครงสร้างช่วงเวลาเดียวกับที่ API เสียงพูด คืน ไฟล์คำบรรยายที่สร้างจากฝั่งหนึ่งจึงใช้กับอีกฝั่งได้พอดี ภาษาไทยไม่เว้นวรรคระหว่างคำ ช่วงเวลาเหล่านี้จึงเป็นทางเดียวที่วางขอบเขตคำบรรยายได้อย่างแม่นยำ ไฟล์ที่ไม่มีเสียงพูดจะได้อาร์เรย์ว่าง
AUDIO=$(base64 < meeting.m4a | tr -d '\n')
curl -X POST https://api.paxalabs.com/v1/stt \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"audio\": \"$AUDIO\", \"model\": \"paxa-stt-lite-v1-preview\", \"timestamps\": \"word\"}"{
"text": "สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน พันสองร้อยห้าสิบ บาทครบกำหนดวันที่ สิบห้า สิงหาคมนี้ชำระผ่านแอปได้ตลอด ยี่สิบสี่ ชั่วโมงค่ะ",
"words": [
{
"text": "สวัสดี",
"start": 0.12,
"end": 0.52
},
{
"text": "ค่ะ",
"start": 0.6,
"end": 0.739
},
{
"text": "คุณ",
"start": 1.64,
"end": 1.8
},
{
"text": "สมชาย",
"start": 1.84,
"end": 2.24
},
{
"text": "ยอด",
"start": 2.4,
"end": 2.56
},
{
"text": "ชำระ",
"start": 2.56,
"end": 2.839
},
{
"text": "ของ",
"start": 2.84,
"end": 2.96
},
{
"text": "ท่าน",
"start": 2.96,
"end": 3.14
},
{
"text": "จำนวน",
"start": 3.16,
"end": 3.44
},
{
"text": "พันสองร้อยห้าสิบ",
"start": 3.52,
"end": 4.56
},
{
"text": "บาท",
"start": 4.68,
"end": 4.84
},
{
"text": "ครบ",
"start": 5,
"end": 5.2
},
{
"text": "กำหนด",
"start": 5.24,
"end": 5.48
},
{
"text": "วัน",
"start": 5.52,
"end": 5.639
},
{
"text": "ที่",
"start": 5.64,
"end": 5.719
},
{
"text": "สิบห้า",
"start": 5.84,
"end": 6.08
},
{
"text": "สิงหาคม",
"start": 6.68,
"end": 7.16
},
{
"text": "นี้",
"start": 7.2,
"end": 7.34
},
{
"text": "ชำระ",
"start": 7.8,
"end": 8.08
},
{
"text": "ผ่าน",
"start": 8.12,
"end": 8.28
},
{
"text": "แอป",
"start": 8.28,
"end": 8.44
},
{
"text": "ได้",
"start": 8.48,
"end": 8.62
},
{
"text": "ตลอด",
"start": 8.64,
"end": 8.88
},
{
"text": "ยี่สิบสี่",
"start": 9.04,
"end": 9.439
},
{
"text": "ชั่วโมง",
"start": 9.56,
"end": 9.88
},
{
"text": "ค่ะ",
"start": 9.92,
"end": 10.06
}
],
"usage": {
"seconds": 10.25,
"credits": 1.43
}
}แบบพูดหรือแบบเขียน#
ฟิลด์ convention เลือกว่าบทถอดจะเขียนสิ่งที่พูดอย่างไร ค่าเริ่มต้นคือ "spoken" ถ้อยคำเหมือนกันทั้งสองแบบ สิ่งที่ต่างคือตัวเลข หน่วย วันที่ และคำซ้ำ ไฟล์เสียงเบื้องหลังตัวอย่างในหน้านี้พูดถึงยอดเงิน วันที่ และช่วงเวลา:
| สิ่งที่พูด | spoken (ค่าเริ่มต้น) | written |
|---|---|---|
| หนึ่งพันสองร้อยห้าสิบบาท | พันสองร้อยห้าสิบบาท | 1,250บาท |
| วันที่สิบห้าสิงหาคม | สิบห้าสิงหาคม | 15สิงหาคม |
| ยี่สิบสี่ชั่วโมง | ยี่สิบสี่ชั่วโมง | 24ชั่วโมง |
บทถอดทั้งประโยคแบบ "spoken" อ่านว่า สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน พันสองร้อยห้าสิบ บาทครบกำหนดวันที่ สิบห้า สิงหาคมนี้ชำระผ่านแอปได้ตลอด ยี่สิบสี่ ชั่วโมงค่ะ ส่วนแบบ "written" อ่านว่า สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน 1,250 บาทครบกำหนดวันที่ 15 สิงหาคมนี้ชำระผ่านแอปได้ตลอด 24 ชั่วโมงค่ะ
"spoken" เขียนตัวเลขตามที่ออกเสียง และคำที่พูดสองครั้งเป็นสองคำ เหมาะกับบันทึกคำต่อคำ คำบรรยายที่ต้องตรงกับเสียง หรือข้อความที่จะป้อนให้โมเดลภาษาอ่าน เพราะแบบพูดเก็บสิ่งที่ผู้พูดพูดไว้ครบถ้วน ส่วน "written" คงรูปแบบการแสดงผล คือตัวเลขกับหน่วย และไม้ยมก ๆ เหมาะกับข้อความบอกเขียนที่จะถูกอ่านบนหน้ากระดาษ เช่น บันทึกหรือข้อความ เวลาระดับคำเหมือนกันทั้งสองแบบ
AUDIO=$(base64 < meeting.m4a | tr -d '\n')
curl -X POST https://api.paxalabs.com/v1/stt \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"audio\": \"$AUDIO\", \"model\": \"paxa-stt-lite-v1-preview\", \"convention\": \"written\"}"{
"text": "สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน 1,250 บาทครบกำหนดวันที่ 15 สิงหาคมนี้ชำระผ่านแอปได้ตลอด 24 ชั่วโมงค่ะ",
"usage": {
"seconds": 10.25,
"credits": 1.43
}
}คำต่อคำหรือเรียบเรียง#
ฟิลด์ style เลือกว่าบทถอดจะตามเสียงใกล้แค่ไหน ค่าเริ่มต้นคือ "verbatim" คำอุทาน คำพูดติดขัด และการพูดแก้ถูกเขียนตามที่พูด ส่วน "clean" ตัดคำอุทานออกและรวมวลีที่ขาดตอนเป็นวลีเดียว เป็นตัวเลือกเพื่อความอ่านง่าย บทถอดแบบเรียบเรียงห่างจากสิ่งที่พูดมากขึ้น และการวัดของเราเองพบว่าตรงน้อยกว่า เลือกสำหรับบทถอดที่ผู้อ่านจะกวาดตาอ่าน บันทึกหลักฐานใช้ค่าเริ่มต้น ค่าอื่นตอบ 400 validation รูปแบบการเขียนใช้ทับบนสไตล์ใดก็ได้
ผู้พูด#
ตั้ง diarization เป็น true แล้วคำตอบจะมีอาร์เรย์ segments หนึ่งรายการต่อหนึ่งช่วงพูด พร้อมหมายเลข speaker ข้อความ text และเวลาเริ่มกับสิ้นสุดเป็นวินาที ผู้พูดนับจาก 0 ตามลำดับที่ปรากฏครั้งแรกในไฟล์ ไม่มีการระบุตัวตนของใคร ข้อความของแต่ละช่วงเป็นส่วนหนึ่งของบทถอด ทั้งสองจึงไม่มีวันขัดกัน หากตั้ง timestamps เป็น "word" ด้วย ทุกช่วงคำจะระบุ speaker ของตัวเอง ไฟล์ที่แยกผู้พูดถูกอ่านรวดเดียวแทนการอ่านเป็นท่อนขนานกัน หมายเลขผู้พูดจึงตรงกันทั้งไฟล์ การอ่านรวดเดียวนี้เองคือข้อจำกัด ไฟล์ที่แยกผู้พูดต้องสั้นกว่า 9 นาที ไฟล์ตั้งแต่ความยาวนั้นขึ้นไปตอบ 400 diarization_audio_too_long ก่อนตัดเครดิต และใช้เวลาตอบนานกว่าไฟล์ที่ไม่แยกผู้พูด ไฟล์ที่ยาวกว่านี้ให้ตัดตรงช่วงเงียบ แล้วอ่านหมายเลขผู้พูดแยกกันในแต่ละท่อน เพราะแต่ละท่อนนับจาก 0 ใหม่ ไฟล์เสียงเบื้องหลังตัวอย่างในหน้านี้มีเสียงเดียว จึงคืนมาเป็นช่วงเดียว:
AUDIO=$(base64 < meeting.m4a | tr -d '\n')
curl -X POST https://api.paxalabs.com/v1/stt \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"audio\": \"$AUDIO\", \"model\": \"paxa-stt-lite-v1-preview\", \"diarization\": true}"{
"text": "สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน พันสองร้อยห้าสิบ บาทครบกำหนดวันที่ สิบห้า สิงหาคมนี้ชำระผ่านแอปได้ตลอด ยี่สิบสี่ ชั่วโมงค่ะ",
"segments": [
{
"speaker": 0,
"text": "สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน พันสองร้อยห้าสิบ บาทครบกำหนดวันที่ สิบห้า สิงหาคมนี้ชำระผ่านแอปได้ตลอด ยี่สิบสี่ ชั่วโมงค่ะ",
"start": 0.12,
"end": 10.06
}
],
"usage": {
"seconds": 10.25,
"credits": 1.43
}
}คลังคำศัพท์#
ฟิลด์ vocabulary คือรายการคำที่น่าจะปรากฏในไฟล์และต้องการให้โมเดลรู้จำตามรูปเขียนนั้น เช่น ชื่อสินค้า ชื่อคน ชื่อสถานที่ ศัพท์เฉพาะ ทั้งไทยและอังกฤษ ไม่เกิน 50 คำ คำละไม่เกิน 50 ตัวอักษร รายการหรือคำที่ยาวกว่านั้นตอบ 400 validation คำเหล่านี้ช่วยโน้มการรู้จำ ไม่เคยถูกแทรกเข้าไปเอง คำที่ไม่ได้พูดจะไม่ปรากฏ นี่คือคันโยกสำหรับเนื้อหาเฉพาะทาง การประชุมเรื่องสินค้าของคุณเองจะถอดชื่อสินค้าออกมาตามที่คุณสะกด คลังคำศัพท์ใช้ร่วมกับทุกตัวเลือกอื่นได้
AUDIO=$(base64 < meeting.m4a | tr -d '\n')
curl -X POST https://api.paxalabs.com/v1/stt \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"audio\": \"$AUDIO\", \"model\": \"paxa-stt-lite-v1-preview\", \"language\": \"th\", \"vocabulary\": [\"Paxa Labs\", \"quarterly review\"]}"ความเสถียรและการส่งซ้ำ#
คำตอบเป็น JSON ก้อนเดียว คลิปสิบวินาทีได้คำตอบในไม่กี่วินาที ส่วนไฟล์ยาวหนึ่งชั่วโมงอาจใช้เวลาหนึ่งถึงสองนาที เพราะระบบอ่านไฟล์เป็นหลายท่อนขนานกันโดยตัดตรงช่วงเงียบ เวลารอจึงโตช้ากว่าความยาว ตั้งไทม์เอาต์ของไคลเอนต์ให้เผื่อไว้ ตัวอย่างด้านบนเผื่อ 300 วินาที
คำขอที่ใช้เวลานานพอจะเขียนส่วนหัวของคำตอบออกไปก่อนที่จะรู้ผลลัพธ์ เพื่อไม่ให้พร็อกซีระหว่างทางตัดการเชื่อมต่อที่ยังทำงานอยู่ คำตอบลักษณะนี้มีสถานะ 200 เสมอ และรายงานความล้มเหลวไว้ในเนื้อคำตอบด้วยเอกสารปัญหาชุดเดียวกับที่คำขอล้มเหลวได้รับ คือมี title และ status แทนที่ตำแหน่งของ text ไฟล์ยาวคือกรณีที่ไปถึงจุดนี้ ให้ถือว่าเนื้อคำตอบที่มี title คือข้อผิดพลาดตามที่ระบุ ส่วนไฟล์ที่สั้นกว่านั้นแจ้งสถานะไว้ที่บรรทัดสถานะเสมอ
ส่งซ้ำได้อย่างปลอดภัยด้วย Idempotency-Key การส่งซ้ำของคำขอที่ส่งมอบแล้วจะได้บทถอดเดิมโดยไม่ตัดเครดิตซ้ำ คีย์ครอบคลุมทั้งไฟล์เสียงและทุกตัวเลือก การส่งซ้ำด้วยรูปแบบการเขียน สไตล์ หรือคลังคำศัพท์ที่ต่างไปจึงถูกปฏิเสธ เมื่อถอดเสียงไม่สำเร็จ ระบบคืนเครดิตให้ และคำขอใหม่ต้องใช้คีย์ใหม่