API
Manzil: https://console.gapi.uz. Har bir so‘rov Authorization: Bearer <token> sarlavhasi bilan. Tokenlar kabinetning «API tokenlar» bo‘limida yaratiladi va loyihaga bog‘langan.
Javoblar JSON da. Xato shunday ko‘rinadi:
{"error": "insufficient_balance", "hint": "balance 3.20 g (0.00 g held by jobs in flight), this job needs 4.71 g; the beta balance is topped up to 240 g at midnight Asia/Tashkent"}Vazifalar
POST /v1/jobs: yozuvni yuklash
multipart/form-data:
| Maydon | Bu nima |
|---|---|
file | Audio: mp3, wav, ogg/opus, m4a, flac, webm. 200 MiB gacha |
settings | Loyiha sozlamalari ustidan shu so‘rov uchun sozlamalar JSON, ixtiyoriy |
vocabulary | Loyiha lug‘ati o‘rniga shu so‘rov uchun atamalar JSON massivi, ixtiyoriy |
webhook_url | Tayyor bo‘lganda xabar yuboriladigan manzil, ixtiyoriy |
Javob 202: {"id", "state": "queued", "class": "batch", "estimated_seconds", "created_at", "result_url"}. result_url to‘liq manzil — uni saqlab, shundayligicha chaqirsa bo‘ladi. class oddiy yuklashda batch, POST /v1/transcribe da interactive.
Formatlar ro‘yxati to‘liq emas: ffmpeg o‘qiydigan hamma narsa qabul qilinadi, xom AAC oqimi ham. Dekodlanmagan fayl 422 audio_unreadable oladi.
Yozuvni yuklash shart emas: agar u allaqachon havolada tursa, havolani yuboring, biz o‘zimiz yuklab olamiz. Bu telefoniya integratsiyalari uchun qulay — u yerda yozuv baribir omborda yotadi.
curl -X POST https://console.gapi.uz/v1/jobs \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"url": "https://example.com/call.mp3", "settings": {"emotion": false}}'Uchinchi yo‘l — faylning o‘zini multipart siz so‘rov tanasida yuborish, nomini X-File-Name sarlavhasida ko‘rsatib. Unda sozlamalar loyihadan olinadi.
Xuddi shu uchta shaklni POST /v1/transcribe ham qabul qiladi.
GET /v1/jobs/{id}: holat
state: queued, running, done, failed, cancelled. Davom etayotganda progress_seconds bor. Muvaffaqiyatsizlikda error bor.
GET /v1/jobs/{id}/events: hodisalar
text/event-stream. Bu shunchaki jarayon ko‘rsatkichi emas: replikalar tayyor bo‘lgani sari shu yerga keladi, shuning uchun matnni yozuv tugashini kutmay, suhbat davomida ko‘rsatsa bo‘ladi.
| Hodisa | Nima bor |
|---|---|
segments | Tayyor replikalar massivi, tartib bilan, natijadagi ko‘rinishda |
state | Vazifa holatini o‘zgartirdi: data da GET /v1/jobs/{id} qaytaradigan vazifaning o‘zi |
done | Vazifa tugadi; oqim yopiladi. data da {"state", "job"}, tayyorida esa yana result_url |
event: segments
data: [{"index": 0, "start": 0.42, "end": 3.9, "speaker": 1, "text": "Assalomu alaykum…"}]Kech ulangan avval tayyor bo‘lgan hammasini, keyin yangisini oladi — demak boshini o‘tkazib yuborib bo‘lmaydi.
done dan oldingi oxirgi to‘plam replikalarni yana bir marta, endi yakuniy ko‘rinishda yuboradi: yozuv ketayotganda ulardagi tinish belgilari vaqtinchalik. Shuning uchun replikalarni index bo‘yicha yig‘ing, avval kelganini almashtirib — shunda oqim oxirida sizda aynan natijadagi matn bo‘ladi.
Har yigirma soniyada oqimga : keepalive izoh qatori ketadi, proksi ulanishni yopmasin uchun. Oqim uzilsa, hech narsa yo‘qolmaydi: qaytadan obuna bo‘ling yoki natijani butunlayicha oling.
GET /v1/jobs/{id}/result: natija
Javob formati bo‘limida tasvirlangan. Natija 30 kun saqlanadi.
GET /v1/jobs: ro‘yxat
{"jobs": [{"id": "41f79b78-…", "state": "done", "class": "batch", "estimated_seconds": 283,
"audio_seconds": 282.7, "created_at": "…", "finished_at": "…"}]}Loyihaning oxirgi 50 vazifasi, yangisi yuqorida; ?limit= uni 200 tagacha ko‘taradi. Maksimumdan katta son tashlab yuborilmaydi, balki qisiladi: limit=500 ham o‘sha 200 tani qaytaradi. Sahifalab o‘tish yo‘q: bu arxivni yuklab olish emas, yaqin o‘tmishga qarash. O‘chirilgan vazifalar ro‘yxatda ko‘rinmaydi.
DELETE /v1/jobs/{id}: bekor qilish yoki o‘chirish
Bitta so‘rov ikkovidan hozir ma’noga egasini bajaradi.
Navbatdagi yoki ishlayotgan vazifa — bekor qilinadi. Holat cancelled bo‘ladi, hisobdan hech narsa yechilmaydi, job.cancelled webhooki keladi. Javobda vazifaning o‘zi. Bekor qilish — ishni to‘xtatish, unutish emas: vazifa ro‘yxatda qoladi.
Allaqachon tugagan vazifa — o‘chiriladi. Javob {"id", "state", "deleted": true}, va shu daqiqadan u yo‘q: na ro‘yxatda, na GET /v1/jobs/{id} da, na natijada, na hodisalar oqimida; takroriy DELETE 404 job_not_found javobini beradi. Ham bekor qilish, ham o‘chirish uchun DELETE ni ikki marta chaqiring.
O‘chirish vazifani sizdan yashiradi, lekin bizda o‘chirmaydi: u uchun yechilgan pul hisob tarixida qoladi, yozuv va matn esa odatdagi saqlash muddatini yashab bo‘ladi. Aks holda hisobni tushuntirib bo‘lmasdi.
POST /v1/transcribe: qisqa va darhol
POST /v1/jobs bilan bir xil maydonlar, 5 daqiqagacha yozuvlar uchun. 200 javob bu natijaning o‘zi, vazifa identifikatori X-Job-Id sarlavhasida. 3 daqiqada natija tayyor bo‘lmasa, job_id bilan 202 keladi: keyin GET /v1/jobs/{id} orqali so‘rang.
So‘rov uchun sozlamalar
settings maydoni kabinetning «Sozlamalar» bo‘limidagi kalitlarni qabul qiladi; ko‘rsatilmaganlari loyihadan olinadi.
{
"formatting": true,
"itn": true,
"word_timestamps": false,
"stereo_channels_as_speakers": true,
"diarization": true,
"max_speakers": 2,
"gender": true,
"speaker_roles": true,
"call_direction": "inbound",
"speaker_identification": true,
"emotion": false,
"pii": false
}webhook_url ham shu yerga qo‘yiladi, agar uni qolgan sozlamalar yonida saqlash qulay bo‘lsa; so‘rovning alohida maydoni bo‘lsa, u kuchliroq.
Kalitlar tekshiriladi: mavjud bo‘lmagani yuklashni 400 unknown_setting bilan rad etadi, maslahat esa uni nomma-nom aytadi. Ya’ni diarization o‘rniga diarisation — darhol xato, hisobdan oddiy matn kabi yechilgan oddiy matn emas.
Har bir kalit nima qiladi: Opsiyalar.
Loyiha lug‘ati
Kabinetdagi «Lug‘at» bo‘limidagi atamalarning o‘zi: kompaniya va mahsulot nomlari, ismlar. Sukut bo‘yicha ro‘yxat loyihaga tegishli: vazifa yaratilganda faol versiyaning nusxasini oladi, shuning uchun lug‘atni tahrirlash allaqachon yuborilgan ishni qayta yozmaydi. Alohida yozuv o‘z ro‘yxati bilan ham kelishi mumkin — quyida.
| So‘rov | Nima qiladi |
|---|---|
GET /v1/vocabulary | Faol lug‘at: terms, version, updated_at, min_terms, max_terms |
PUT /v1/vocabulary {"terms": [...]} | Yangi versiyani saqlab, uni yoqadi; bo‘sh massiv lug‘atni olib tashlaydi |
curl -X PUT https://console.gapi.uz/v1/vocabulary \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"terms": ["MetaSell", "Bitrix24", "amoCRM", "Toshkent"]}'1 tadan 20 tagacha atama, har biri ko‘pi bilan 4 so‘z va 80 belgi. Takrorlar va bo‘sh satrlar indamay tashlab yuboriladi, tartib saqlanadi; takror harf katta-kichikligiga qaramay aniqlanadi, ya’ni «metasell» va «MetaSell» bitta atama va birinchisi qoladi. Ro‘yxat qanchalik qisqa bo‘lsa, model kerakli so‘zni shunchalik aniq topadi, shuning uchun faqat yozuvlaringizda haqiqatan yangraydiganini yozing. Xatolar: term_too_long, term_too_many_words, too_many_terms.
Har bir saqlash — yangi raqamlangan versiya va eng oxirgisi faol bo‘ladi, ya'ni noto‘g‘ri tahrir yo‘qotish emas, bir qadam orqaga. Versiyalar tarixi kabinetda ko‘rinadi.
Bitta so‘rov uchun lug‘at
Atamalar faqat shu yozuv uchun kerak bo‘lsa — birovning katalogini qayta ishlayapsiz yoki ro‘yxatni saqlashdan oldin sinab ko‘ryapsiz — ularni fayl bilan birga yuboring:
curl -X POST https://console.gapi.uz/v1/jobs \
-H "Authorization: Bearer $TOKEN" \
-F file=@call.mp3 \
-F 'vocabulary=["MetaSell", "Bitrix24", "amoCRM"]'Ro‘yxat shu vazifa uchun va faqat shuning uchun loyiha lug‘atining o‘rnini bosadi: faol versiya o‘zgarmaydi, keyingi yozuv yana loyiha lug‘ati bilan ketadi. Bo‘sh massiv «bu yozuvni umuman lug‘atsiz hisobla» degani. Qoidalar va xato kodlari PUT dagidek, noto‘g‘ri atama esa vazifa paydo bo‘lishidan oldin yuklashni rad etadi.
JSON shaklidagi yuklashda maydon xuddi shunday ataladi: "vocabulary": [...]. Fayl so‘rov tanasi sifatida yuborilsa, lug‘at loyihadan olinadi. POST /v1/transcribe bu maydonni POST /v1/jobs bilan barobar qabul qiladi.
Nima qo‘llangani natijada ko‘rinadi: vocabulary_terms — vazifa nechta atama olgani, vocabulary_dropped — tozalashdan keyin bittasi ham qolmasa, true.
Loyiha ovozlari
| So‘rov | Nima qiladi |
|---|---|
GET /v1/speakers | Ovozlar ro‘yxati: id, number, name (ismsizlarda bo‘sh), voiceprints, shuningdek kind, seen_count, last_seen_at, expires_at, has_sample |
POST /v1/speakers {"name"} | Izsiz ovoz yaratish |
PATCH /v1/speakers/{id} {"name"} | Ism berish yoki o‘zgartirish |
DELETE /v1/speakers/{id} | Ovozni barcha izlari bilan o‘chirish |
POST /v1/speakers/{id}/enroll | Tayyor yozuvdan iz qo‘shish: {"job_id", "speaker_index"} yoki {"job_id", "segment_indexes": [...]} |
DELETE /v1/speakers/{id}/voiceprints | Barcha izlarni o‘chirish, ismni qoldirish |
GET /v1/speakers/{id}/sample | Bu ovoz eshitiladigan o‘n ikki soniyagacha audio (audio/wav; odatda qisqaroq — eng uzun replika olinadi). Bu ovozning saqlangan yozuvi bo‘lmasa 404 no_sample, yozuvning o‘zi saqlanmasa 410 audio_expired |
DELETE /v1/speakers?kind=candidate | Bir turdagi barcha ovozlarni birdan o‘chirish: candidate, recurring yoki named |
Batafsil: Ovozlar va so‘zlovchilar.
Foydalanish
GET /v1/usage g dagi balans va kunlik to‘ldirish maqsadini qaytaradi:
{"unit": "g", "balance_g": 203.63, "in_flight_g": 4.5, "available_g": 199.13, "topup_target_g": 240,
"next_topup": "00:00 Asia/Tashkent", "day": "2026-09-09", "used_seconds": 3425.6, "remaining_seconds": 11947.8}GET /v1/billing balans hamda to‘ldirish va yechishlar tarixini qaytaradi. Narx qanday hisoblanadi: Balans va to‘lov.
Webhooklar
Loyihada webhook_url berilgan yoki so‘rovda uzatilgan bo‘lsa, vazifa tugagach shu manzilga JSON bilan POST keladi:
{"event": "job.done", "id": "41f79b78-…", "state": "done", "class": "batch", "estimated_seconds": 283,
"audio_seconds": 282.7, "error": null, "created_at": "…", "finished_at": "…",
"result_url": "https://console.gapi.uz/v1/jobs/41f79b78-…/result"}event bu job. va holat: job.done, job.failed, job.cancelled — bitta switch xatni ajratadi. audio_seconds bu hisobdan yechilgan yozuv uzunligi; yiqilgan vazifada u yo‘q.
Webhook vazifaning har qanday yakunida keladi, faqat muvaffaqiyatlisida emas: state done, failed va cancelled bo‘lishi mumkin, demak bekor qilingan yoki yiqilgan vazifa ham kutishni yopadi, sizning tomondagi taymaut esa faqat biz yeta olmagan holat uchun kerak.
Birinchi so‘rov vazifa tugagan zahoti ketadi: u fondagi ko‘rikni kutmaydi — vazifaning yakunlanishi yetkazishni o‘zi uyg‘otadi.
Istalgan 2xx bilan javob bering, javobga o‘n soniya beriladi. Har bir yetkazish o‘z ulanishini ochadi va uni yopadi: biz sizning tomoningizda ulanishni ushlab turmaymiz.
Xato bo‘lsa yetkazish 5 martagacha takrorlanadi, tanaffus esa oldingi urinishdan emas, vazifaning tugashidan hisoblanadi: u tugagach taxminan 30 s, 2 daq, 4.5 daq va 8 daq. Takroriy urinishlar daqiqada bir marta ko‘rib chiqiladi, shuning uchun bu sonlar taxminiy — birinchi so‘rovdan farqli o‘laroq.
O‘chirilgan vazifa webhook yubormaydi.
Limitlar
| Nima | Qancha |
|---|---|
| Fayl hajmi | 200 MiB (209 715 200 bayt) |
| Yozuv uzunligi | 4 soatgacha |
Sinxron so‘rov /v1/transcribe | 5 daqiqagacha yozuvlar |
| Bir tokenga daqiqasiga yuklashlar | 60 |
| Bir tokenga daqiqasiga o‘qish so‘rovlari | 3000 |
| Bir tokenga daqiqasiga boshqaruv so‘rovlari | 600 |
| Natijani saqlash | 30 kun |
| Balans | har kuni Toshkent vaqti bilan yarim tunda 240 g gacha to‘ldiriladi |
| Hisobda loyihalar, loyihada tokenlar | 3, 10 |
| Loyihada ovozlar, ovozda izlar | 500, 10 |
| Lug‘atdagi atamalar | 1 dan 20 gacha |
Yozuvni yuborish va u haqda so‘rash har xil turadi, shuning uchun limitlar ham har xil: yuklash modeli bor mashinani band qiladi, GET /v1/jobs/{id} esa bazadagi bitta qator. Holatni bemalol so‘rasa bo‘ladi — daqiqasiga 3000 ta bu o‘ttizta vazifa, sekundiga ikki so‘rovdan. Lekin GET /v1/jobs/{id}/events yoki webhook yaxshiroq: unda umuman so‘rash kerak emas.
Chelaklar uchta va alohida-alohida sanaladi. Yuklashlarga POST /v1/jobs va POST /v1/transcribe kiradi. Boshqaruvga — loyihani o‘zgartiradigan, lekin mashinani band qilmaydigan hammasi: ovozlar va lug‘at. Qolgani o‘qish.
429 javobida Retry-After sarlavhasi bor, unda soniyalar soni: oynada joy bo‘shashiga shuncha qoldi. Undan uzoq kutish shart emas, kamroq kutish esa foydasiz.
Bu jadvaldagi sonlar — sukut bo‘yicha qiymatlar, o‘zgarmas emas: to‘ldirish maqsadi, fayl hajmi, uzunlik va loyihadagi bir vaqtdagi vazifalar hisob uchun kelishuv bo‘yicha o‘zgaradi.
Oshib ketganda 429 keladi: rate_limited, insufficient_balance yoki too_many_active_jobs.
Xato kodlari
Kod error maydonida, yonida odam o‘qiydigan hint. Uch guruhni ajratgan ma’qul: so‘rovni tuzatish, kutib qayta urinish va endi qaytmaydigan.
So‘rov shu holida qabul qilinmaydi.
| Kod | HTTP | Qachon |
|---|---|---|
missing_token, invalid_token | 401 | Authorization sarlavhasi yo‘q, yoki token noto‘g‘ri yoki bekor qilingan |
account_blocked | 403 | Hisob bloklangan; bizga yozing |
file_required | 400 | multipart da file maydoni yo‘q |
invalid_multipart, invalid_json | 400 | Tana tahlil qilinmaydi |
invalid_url, fetch_failed | 400, 502 | Havola http(s) emas, tahlil qilinmaydi yoki undagi audio yuklanmadi |
invalid_webhook_url | 400 | webhook_url http(s) emas |
invalid_call_direction, invalid_max_speakers | 400 | Qiymat chegaradan tashqarida: yo‘nalish inbound, outbound yoki bo‘sh; spikerlar 4 tagacha |
unknown_setting | 400 | settings da mavjud bo‘lmagan kalit; maslahat uni nomlaydi va bor kalitlarni sanaydi |
upload_failed | 400 | So‘rov tanasi bo‘sh yoki o‘rtasida uzilgan |
invalid_speaker_name | 400 | Ovoz nomi 1–120 belgidan tashqarida |
missing_job_id | 400 | job_id siz enroll |
speaker_index_out_of_range | 400 | Natijada bunday raqamli spiker yo‘q; maslahat nechtaligini aytadi |
invalid_request | 400 | Qiymat bu ruchka kutgan shaklda emas |
method_not_allowed | 405 | Shu manzil boshqa metodni qabul qiladi; u Allow sarlavhasida aytilgan |
no_voiceprint | 422 | Bu spikerning izi yo‘q: «Ovozdan tanish» yoqilgan va kamida 3 soniya nutq kerak |
insufficient_speech | 422 | Iz uchun 3 soniyadan kam nutq belgilangan |
terms_required | 400 | terms siz PUT /v1/vocabulary |
term_too_long, term_too_many_words, too_many_terms | 400 | Atama 80 belgidan uzun, 4 so‘zdan ko‘p, yoki atamalar 20 tadan ko‘p |
audio_unreadable | 422 | Fayl dekodlanmaydi |
audio_too_long | 422 | Yozuv 4 soatdan uzun |
too_long_for_sync | 422 | 5 daqiqadan uzun yozuv uchun /v1/transcribe; /v1/jobs dan foydalaning |
file_too_large | 413 | 200 MiB dan katta. Hajm Content-Length sarlavhasida ko‘rinsa, javob yuklash tugashini kutmay darhol keladi |
Kutib, qayta urinish.
| Kod | HTTP | Qachon |
|---|---|---|
rate_limited | 429 | So‘rovlar juda ko‘p; Cheklovlar ga qarang |
insufficient_balance | 429 | g dagi balans bu yozuvga yetmaydi; maslahat qancha borligi va to‘ldirish qachonligini aytadi |
too_many_active_jobs | 429 | Loyihada navbatdagi va ishlayotgan vazifalar ruxsat etilgan darajaga yetdi |
result_not_ready | 409 | Vazifa hali ketmoqda; GET /v1/jobs/{id} so‘rang yoki hodisalariga obuna bo‘ling |
no_runner_for_diarization, no_runner_for_options | 503 | Kerakli modeli bor bo‘sh mashina yo‘q; keyinroq takrorlang |
storage_full | 507 | Bizning tomonda joy tugadi; buni ko‘rib turibmiz |
Endi qaytmaydi.
| Kod | HTTP | Qachon |
|---|---|---|
job_not_found | 404 | Bu loyihada bunday vazifa yo‘q — hech qachon bo‘lmagan, o‘chirilgan yoki id boshqa |
speaker_not_found | 404 | Bu loyihada bunday ovoz yo‘q |
job_result_not_found | 404 | enroll tayyor bo‘lmagan, bu loyihaga tegishli bo‘lmagan yoki o‘chirilgan vazifaga ishora qiladi |
no_sample | 404 | Bu ovozning saqlangan yozuvi yo‘q |
not_found | 404 | API da bunday manzil yo‘q |
result_expired | 410 | Natija 30 kundan oshdi va o‘chirildi |
audio_expired | 410 | Yozuvning o‘zi endi saqlanmaydi: ovoz namunasini eshittirib bo‘lmaydi |
Alohida internal va 500: bu sizniki emas, bizning nosozligimiz. Bunday javobni takrorlash arziydi, agar u takrorlanaversa — bizga yozing, biz uni o‘z loglarimizda ko‘ramiz.
Noto‘g‘ri id — UUID emas, shablondan kelgan bo‘sh o‘zgaruvchi — bu 500 emas, 404: bunday vazifa shunchaki yo‘q.