Repository Public
1The Gemini Live API supports low-latency, real-time speech-to-text transcription using the [`gemini-3.5-transcribe-live`](https://ai.google.dev/gemini-api/docs/models/gemini-3.5-transcribe) model. By connecting to the Live API over WebSockets or using the Google Gen AI SDK, you can stream continuous audio input and receive incremental, real-time text transcriptions as speech occurs.2[Try Live Transcription in Google AI Studio](https://aistudio.google.com/live?model=gemini-3.5-transcribe-live) [Open the Colab cookbook](https://github.com/google-gemini/cookbook) [Use coding agent skills](https://ai.google.dev/gemini-api/docs/coding-agents#gemini-live-api-dev)34By leveraging the Gemini Live API, developer platforms such as5[Agora](https://docs.agora.io/en/ai/models/asr/gemini),6[Fishjam](https://docs.fishjam.io/tutorials/gemini-live-integration),7[LiveKit](https://docs.livekit.io/agents/models/stt/gemini/),8[Pipecat](https://docs.pipecat.ai/api-reference/server/services/stt/google),9[Vercel](https://vercel.com/docs/ai-gateway/modalities/speech-to-text), and10[Vision Agents](https://visionagents.ai/integrations/stt/gemini)11enable developers to build and deploy high-performance voice-driven interfaces12with ease. These platforms manage complex real-time media streaming13infrastructure behind the scenes, allowing developers to focus entirely on14crafting the user experience.1516## Live agent versus live Transcription1718While both use the Live API bidirectional streaming connection, Live Transcription operates as a dedicated, low-latency speech recognition pipeline rather than a conversational agent.1920| Feature | Live Agent | Live Transcription |21|---|---|---|22| **Primary role** | Conversational assistant that listens, reasons, and speaks back. | Real-time speech-to-text pipeline that transcribes incoming audio. |23| **Response modality** | Spoken audio and text (`response_modalities=["AUDIO"]`). | Streaming text transcriptions (`response_modalities=["TEXT"]`). |24| **Interaction style** | Turn-based dialogue with pause detection and interruptions. | Continuous stream processing as the speaker talks. |25| **Supported features** | Function calling, Google Search, system instructions. | Speech biasing (`custom_vocabulary`), language detection, manual \& hybrid VAD, Smart transcription. |26| **Input stream** | Multimodal: audio, video, images, text. | Audio input (raw 16-bit PCM). |2728## Get started2930The following examples demonstrate how to open a bidirectional streaming session with `gemini-3.5-transcribe-live` and receive real-time transcriptions.3132### Python3334 import asyncio35 from google import genai36 from google.genai import types3738 client = genai.Client()39 model = "gemini-3.5-transcribe-live"4041 config = types.LiveConnectConfig(42 response_modalities=["TEXT"],43 input_audio_transcription=types.AudioTranscriptionConfig(44 language_codes=[], # Automatic language detection45 ),46 )4748 async def main():49 async with client.aio.live.connect(model=model, config=config) as session:50 print("Session established with Live Transcription")5152 # Receive transcription events53 async for response in session.receive():54 server_content = response.server_content55 if server_content and server_content.input_transcription:56 print("Transcript:", server_content.input_transcription.text)5758 if __name__ == "__main__":59 asyncio.run(main())6061### JavaScript6263 import { GoogleGenAI, Modality } from '@google/genai';6465 const ai = new GoogleGenAI({});66 const model = 'gemini-3.5-transcribe-live';6768 const config = {69 responseModalities: [Modality.TEXT],70 inputAudioTranscription: {71 languageCodes: [], // Automatic language detection72 },73 };7475 async function main() {76 const session = await ai.live.connect({77 model: model,78 config: config,79 callbacks: {80 onopen: () => console.log('Connected to Live Transcription'),81 onmessage: (message) => {82 const content = message.serverContent;83 if (content?.inputTranscription) {84 console.log('Transcript:', content.inputTranscription.text);85 }86 },87 onerror: (e) => console.error('Error:', e.message),88 onclose: (e) => console.log('Connection closed:', e.reason),89 },90 });91 }9293 main();9495### WebSockets9697 const API_KEY = "YOUR_API_KEY";98 const MODEL_NAME = "gemini-3.5-transcribe-live";99 const WS_URL = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key=${API_KEY}`;100101 const websocket = new WebSocket(WS_URL);102103 websocket.onopen = () => {104 console.log('WebSocket connected');105106 const setupMessage = {107 setup: {108 model: `models/${MODEL_NAME}`,109 generationConfig: {110 responseModalities: ['TEXT'],111 },112 inputAudioTranscription: {113 languageCodes: []114 }115 }116 };117 websocket.send(JSON.stringify(setupMessage));118 };119120 websocket.onmessage = (event) => {121 const response = JSON.parse(event.data);122 const content = response.serverContent;123 if (content?.inputTranscription) {124 console.log('Transcript:', content.inputTranscription.text);125 }126 };127128## Interim and finalized transcriptions129130As audio streams into the Live API, the server emits two complementary transcription fields within `server_content`:131132- **`interim_input_transcription`** : low-latency, speculative partial hypotheses updated while the speaker is actively talking. These partial updates occur rapidly with minimal delay. Use `interim_input_transcription` to render responsive live UI subtitles or preview captions.133- **`input_transcription`**: the finalized transcript emitted when the speaker pauses, the turn completes, or speech is finalized. Once emitted, this text represents the model's authoritative transcription of that speech segment. In smart transcription mode, this will include the cleaned, formatted response.134135The following example demonstrates how to display streaming interim partials and commit final transcripts:136137### Python138139 async def receive_transcripts(session):140 async for response in session.receive():141 server_content = response.server_content142 if not server_content:143 continue144145 # Real-time interim hypothesis (updates dynamically as user speaks)146 if server_content.interim_input_transcription:147 interim_text = server_content.interim_input_transcription.text148 print(f"\r[Interim] {interim_text}", end="", flush=True)149150 # Finalized transcript (emitted on speech completion)151 if server_content.input_transcription:152 final_text = server_content.input_transcription.text153 print(f"\n[Final] {final_text}")154155### JavaScript156157 onmessage: (message) => {158 const content = message.serverContent;159 if (!content) return;160161 if (content.interimInputTranscription) {162 // Update live subtitle preview on screen163 renderInterimPreview(content.interimInputTranscription.text);164 }165166 if (content.inputTranscription) {167 // Append final committed transcript to chat history168 commitFinalTranscript(content.inputTranscription.text);169 }170 };171172### WebSockets173174 websocket.onmessage = (event) => {175 const response = JSON.parse(event.data);176 const content = response.serverContent;177 if (content?.interimInputTranscription) {178 console.log('[Interim]:', content.interimInputTranscription.text);179 }180 if (content?.inputTranscription) {181 console.log('[Final]:', content.inputTranscription.text);182 }183 };184185## Sending audio186187Stream audio chunks over the active connection as raw 16-bit PCM audio.188189- **Audio format:** Raw 16-bit PCM at 16kHz (mono, little-endian).190- **Chunk size:** Send audio in chunks of 100ms (1,024 to 2,048 frames).191- **MIME type:** `audio/pcm;rate=16000` (or the matching sample rate).192193### Python194195 # Stream a raw PCM audio chunk196 await session.send_realtime_input(197 audio=types.Blob(198 data=audio_chunk_bytes,199 mime_type="audio/pcm;rate=16000"200 )201 )202203 # Signal the end of the audio stream when finished204 await session.send_realtime_input(audio_stream_end=True)205206### JavaScript207208 // Send base64-encoded PCM audio chunk209 session.sendRealtimeInput({210 audio: {211 data: audioChunkBase64,212 mimeType: 'audio/pcm;rate=16000'213 }214 });215216 // Signal stream end217 session.sendRealtimeInput({218 audioStreamEnd: true219 });220221### WebSockets222223 // Send base64-encoded PCM audio chunk224 websocket.send(JSON.stringify({225 realtimeInput: {226 audio: {227 data: audioChunkBase64,228 mimeType: 'audio/pcm;rate=16000'229 }230 }231 }));232233 // Signal stream end234 websocket.send(JSON.stringify({235 realtimeInput: {236 audioStreamEnd: true237 }238 }));239240## Transcription features241242### Automatic language detection243244By default, omitting `language_codes` or setting `language_codes=[]` enables automatic language identification. The model dynamically detects the spoken language across utterances, including multilingual conversations and code-switching.245246### Python247248 config = types.LiveConnectConfig(249 response_modalities=["TEXT"],250 input_audio_transcription=types.AudioTranscriptionConfig(251 language_codes=[],252 ),253 )254255### JavaScript256257 const config = {258 responseModalities: [Modality.TEXT],259 inputAudioTranscription: {260 languageCodes: [],261 },262 };263264### WebSockets265266 const setupMessage = {267 setup: {268 model: 'models/gemini-3.5-transcribe-live',269 generationConfig: {270 responseModalities: ['TEXT'],271 },272 inputAudioTranscription: {273 languageCodes: [],274 },275 },276 };277 websocket.send(JSON.stringify(setupMessage));278279### Specific language hint280281Provide explicit BCP-47 language codes (for example, `["es-ES"]` for Spanish or `["fr-FR"]` for French) to bias recognition toward specific languages (see [Supported languages](https://ai.google.dev/gemini-api/docs/live-api/live-transcribe#supported-languages)).282283### Python284285 config = types.LiveConnectConfig(286 response_modalities=["TEXT"],287 input_audio_transcription=types.AudioTranscriptionConfig(288 language_codes=["es-ES"],289 ),290 )291292### JavaScript293294 const config = {295 responseModalities: [Modality.TEXT],296 inputAudioTranscription: {297 languageCodes: ['es-ES'],298 },299 };300301### WebSockets302303 const setupMessage = {304 setup: {305 model: 'models/gemini-3.5-transcribe-live',306 generationConfig: {307 responseModalities: ['TEXT'],308 },309 inputAudioTranscription: {310 languageCodes: ['es-ES'],311 },312 },313 };314 websocket.send(JSON.stringify(setupMessage));315316### Custom vocabulary biasing317318Provide a list of up to 1,000 phrases, proper nouns, brand names, or technical terms in `custom_vocabulary` to bias speech recognition toward specific terminology (best results are typically achieved with up to 100 terms).319320### Python321322 config = types.LiveConnectConfig(323 response_modalities=["TEXT"],324 input_audio_transcription=types.AudioTranscriptionConfig(325 language_codes=[],326 custom_vocabulary=["Gemini", "Kubernetes", "BigQuery"],327 ),328 )329330### JavaScript331332 const config = {333 responseModalities: [Modality.TEXT],334 inputAudioTranscription: {335 languageCodes: [],336 customVocabulary: ['Gemini', 'Kubernetes', 'BigQuery'],337 },338 };339340### WebSockets341342 const setupMessage = {343 setup: {344 model: 'models/gemini-3.5-transcribe-live',345 generationConfig: {346 responseModalities: ['TEXT'],347 },348 inputAudioTranscription: {349 languageCodes: [],350 customVocabulary: ['Gemini', 'Kubernetes', 'BigQuery'],351 },352 },353 };354 websocket.send(JSON.stringify(setupMessage));355356### Smart transcription357358Configure transcription output formatting using the `mode` parameter in `input_audio_transcription`:359360- **`VERBATIM` (default)**: Produces an exact literal transcript of everything spoken, preserving raw filler words ("um", "uh", "like"), repetitions, and false starts.361- **`SMART` (Smart transcription)**: Cleans up and structures the transcript for readability:362363 - **Disfluency removal**: Removes filler words, stuttering, and false starts.364 - **Inline self-corrections**: Resolves spoken corrections naturally.365 - **Structured formatting**: Automatically formats lists, bullet points, numbers, dates, and paragraph breaks.366 - **Grammar \& casing**: Applies natural capitalization and punctuation polish.367368### Python369370 config = types.LiveConnectConfig(371 response_modalities=["TEXT"],372 input_audio_transcription=types.AudioTranscriptionConfig(373 mode="SMART",374 ),375 )376377### JavaScript378379 const config = {380 responseModalities: [Modality.TEXT],381 inputAudioTranscription: {382 mode: 'SMART',383 },384 };385386### WebSockets387388 const setupMessage = {389 setup: {390 model: 'models/gemini-3.5-transcribe-live',391 generationConfig: {392 responseModalities: ['TEXT'],393 },394 inputAudioTranscription: {395 mode: 'SMART',396 },397 },398 };399 websocket.send(JSON.stringify(setupMessage));400401## Voice Activity Detection (VAD) strategies402403### Automatic VAD (Default)404405By default, server-side automatic Voice Activity Detection detects when a speaker begins and stops speaking.406407### Hybrid VAD408409[Hybrid VAD](https://ai.google.dev/gemini-api/docs/live-api/capabilities#hybrid-vad) combines server-side automatic speech start detection with client-side speech end detection for zero-latency turn finalization:4104111. **Server-side automatic VAD remains enabled** to detect speech beginnings accurately with prefix audio padding, preventing front-word truncation.4122. **Client-side VAD detects silence** : When a local on-device VAD detects that the speaker has stopped talking, the client sends an `audio_stream_end` signal immediately.4133. **Fast finalization** : The server treats `audio_stream_end` as an immediate turn finalization prompt, bypassing the default server-side silence wait time and returning the finalized transcript with minimal latency.4144. **Fallback**: If the client VAD fails to trigger, the server-side VAD acts as an automatic fallback.415416### Python417418 config = types.LiveConnectConfig(419 response_modalities=["TEXT"],420 input_audio_transcription=types.AudioTranscriptionConfig(),421 )422423 async with client.aio.live.connect(model=model, config=config) as session:424 # Stream audio chunks...425 await session.send_realtime_input(426 audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000")427 )428429 # When client-side VAD detects end of speech, send audio_stream_end:430 await session.send_realtime_input(audio_stream_end=True)431432### JavaScript433434 const config = {435 responseModalities: [Modality.TEXT],436 inputAudioTranscription: {},437 };438439 // Stream audio...440 session.sendRealtimeInput({441 audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' }442 });443444 // When client VAD detects end of speech, send audioStreamEnd:445 session.sendRealtimeInput({446 audioStreamEnd: true447 });448449### WebSockets450451 const setupMessage = {452 setup: {453 model: 'models/gemini-3.5-transcribe-live',454 generationConfig: {455 responseModalities: ['TEXT'],456 },457 inputAudioTranscription: {},458 },459 };460 websocket.send(JSON.stringify(setupMessage));461462 // Stream audio...463 websocket.send(JSON.stringify({464 realtimeInput: {465 audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' }466 }467 }));468469 // When client VAD detects end of speech, send audioStreamEnd:470 websocket.send(JSON.stringify({471 realtimeInput: {472 audioStreamEnd: true473 }474 }));475476### Manual VAD (Push-to-Talk)477478For walkie-talkie interfaces or push-to-talk buttons, disable automatic VAD entirely and control turn boundaries explicitly using `activity_start` and `activity_end`:479480### Python481482 config = types.LiveConnectConfig(483 response_modalities=["TEXT"],484 realtime_input_config=types.RealtimeInputConfig(485 automatic_activity_detection=types.AutomaticActivityDetection(486 disabled=True487 )488 ),489 input_audio_transcription=types.AudioTranscriptionConfig(),490 )491492 async with client.aio.live.connect(model=model, config=config) as session:493 # Button pressed: signal speech start494 await session.send_realtime_input(activity_start=types.ActivityStart())495496 # Stream audio chunks...497 await session.send_realtime_input(audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000"))498499 # Button released: signal speech end500 await session.send_realtime_input(activity_end=types.ActivityEnd())501502### JavaScript503504 const config = {505 responseModalities: [Modality.TEXT],506 realtimeInputConfig: {507 automaticActivityDetection: {508 disabled: true,509 },510 },511 inputAudioTranscription: {},512 };513514 // Signal speech start515 session.sendRealtimeInput({ activityStart: {} });516517 // Stream audio...518519 // Signal speech end520 session.sendRealtimeInput({ activityEnd: {} });521522### WebSockets523524 const setupMessage = {525 setup: {526 model: 'models/gemini-3.5-transcribe-live',527 generationConfig: {528 responseModalities: ['TEXT'],529 },530 realtimeInputConfig: {531 automaticActivityDetection: {532 disabled: true,533 },534 },535 inputAudioTranscription: {},536 },537 };538 websocket.send(JSON.stringify(setupMessage));539540 // Button pressed: signal speech start541 websocket.send(JSON.stringify({542 realtimeInput: {543 activityStart: {},544 },545 }));546547 // Stream audio...548 websocket.send(JSON.stringify({549 realtimeInput: {550 audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' },551 },552 }));553554 // Button released: signal speech end555 websocket.send(JSON.stringify({556 realtimeInput: {557 activityEnd: {},558 },559 }));560561## Ephemeral tokens in client applications562563For client-to-server applications (such as mobile or web apps streaming directly from a microphone), use [ephemeral tokens](https://ai.google.dev/gemini-api/docs/live-api/ephemeral-tokens) to avoid exposing your API key in client code.564565Create a constrained ephemeral token on your server before initiating the client connection:566567### Python568569 import datetime570 from google import genai571572 client = genai.Client()573 expire_time = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(minutes=30)574575 token = client.auth_tokens.create(576 config={577 "uses": 1,578 "expire_time": expire_time,579 "live_connect_constraints": {580 "model": "gemini-3.5-transcribe-live",581 "config": {582 "response_modalities": ["TEXT"],583 "input_audio_transcription": {584 "language_codes": [],585 },586 },587 },588 }589 )590591### JavaScript592593 import { GoogleGenAI } from '@google/genai';594595 const client = new GoogleGenAI({});596 const expireTime = new Date(Date.now() + 30 * 60 * 1000).toISOString();597598 const token = await client.authTokens.create({599 config: {600 uses: 1,601 expireTime: expireTime,602 liveConnectConstraints: {603 model: 'gemini-3.5-transcribe-live',604 config: {605 responseModalities: ['TEXT'],606 inputAudioTranscription: {607 languageCodes: [],608 },609 },610 },611 },612 });613614### REST615616 curl -X POST "https://generativelanguage.googleapis.com/v1beta/auth_tokens" \617 -H "x-goog-api-key: ${GEMINI_API_KEY}" \618 -H "Content-Type: application/json" \619 -d '{620 "uses": 1,621 "expireTime": "YYYY-MM-DDTHH:MM:SSZ",622 "liveConnectConstraints": {623 "model": "models/gemini-3.5-transcribe-live",624 "config": {625 "responseModalities": ["TEXT"],626 "inputAudioTranscription": {627 "languageCodes": []628 }629 }630 }631 }'632633## Supported languages634635The following languages and BCP-47 language codes are supported for Gemini 3.5 Transcribe Live:636637| Language | BCP-47 Code | Language | BCP-47 Code |638|---|---|---|---|639| Afrikaans | `af-ZA` | Japanese | `ja-JP` |640| Amharic | `am-ET` | Javanese | `jv-ID` |641| Arabic (Egypt) | `ar-EG` | Kabuverdianu | `kea-CV` |642| Armenian | `hy-AM` | Kannada | `kn-IN` |643| Assamese | `as-IN` | Kazakh | `kk-KZ` |644| Azerbaijani | `az-AZ` | Korean | `ko-KR` |645| Belarusian | `be-BY` | Kyrgyz | `ky-KG` |646| Bengali (Bangladesh) | `bn-BD` | Latvian | `lv-LV` |647| Bengali (India) | `bn-IN` | Lingala | `ln-CD` |648| Bosnian | `bs-BA` | Lithuanian | `lt-LT` |649| Bulgarian | `bg-BG` | Macedonian | `mk-MK` |650| Bulgarian (Aromanian) | `rup-BG` | Malay | `ms-MY` |651| Burmese | `my-MM` | Malayalam | `ml-IN` |652| Cantonese (Traditional) | `yue-Hant-HK` | Maltese | `mt-MT` |653| Catalan | `ca-ES` | Mandarin Chinese (Simplified) | `cmn-Hans-CN` |654| Cebuano | `ceb` | Marathi | `mr-IN` |655| Central Khmer | `km-KH` | Mongolian | `mn-MN` |656| Croatian | `hr-HR` | Nepali | `ne-NP` |657| Czech | `cs-CZ` | Norwegian | `nb-NO` |658| Danish | `da-DK` | Oriya | `or-IN` |659| Dutch | `nl-NL` | Polish | `pl-PL` |660| English (Great Britain) | `en-GB` | Portuguese (Brazil) | `pt-BR` |661| English (India) | `en-IN` | Portuguese (Portugal) | `pt-PT` |662| English (United States) | `en-US` | Punjabi | `pa-IN` |663| Estonian | `et-EE` | Punjabi (Gurmukhi script) | `pa-Guru-IN` |664| Farsi | `fa-IR` | Romanian | `ro-RO` |665| Filipino | `fil-PH` | Russian | `ru-RU` |666| Finnish | `fi-FI` | Serbian | `sr-RS` |667| French | `fr-FR` | Sindhi (Arabic script) | `sd-Arab-IN` |668| Galician | `gl-ES` | Slovak | `sk-SK` |669| Georgian | `ka-GE` | Slovenian | `sl-SI` |670| German | `de-DE` | Spanish (Latin America) | `es-419` |671| Greek | `el-GR` | Spanish (United States) | `es-US` |672| Gujarati | `gu-IN` | Swahili (Kenya) | `sw-KE` |673| Hausa | `ha-NG` | Swedish | `sv-SE` |674| Hebrew | `he-IL` | Tajik | `tg-TJ` |675| Hindi | `hi-IN` | Telugu | `te-IN` |676| Hungarian | `hu-HU` | Thai | `th-TH` |677| Icelandic | `is-IS` | Turkish | `tr-TR` |678| Indian English | `en-IN` | Ukrainian | `uk-UA` |679| Indonesian | `id-ID` | Uzbek | `uz-UZ` |680| Italian | `it-IT` | Vietnamese | `vi-VN` |681682## Parameter reference683684Configure live transcription using fields in `input_audio_transcription` and `realtime_input_config`:685686| Parameter | Type | Description |687|---|---|---|688| `language_codes` | Array of strings | BCP-47 language codes (e.g., `["en-US"]`). If omitted or empty (`[]`), the model automatically detects the language and handles multilingual speech. |689| `custom_vocabulary` | Array of strings | Up to 1,000 custom terms, acronyms, brand names, or proper nouns to bias speech recognition. |690| `mode` | String | Transcription mode: `"VERBATIM"` (default) or `"SMART"` (Smart transcription). When set to `"SMART"`, the model removes filler words, formats lists, and corrects disfluencies. |691| `automatic_activity_detection.disabled` | Boolean | Set to `true` to disable automatic voice activity detection and manually send `activityStart` and `activityEnd` signals. |692693### Server response fields694695| Field | Description |696|---|---|697| `server_content.interim_input_transcription` | Low-latency, interim partial transcription hypothesis emitted continuously while the user is actively speaking. |698| `server_content.input_transcription` | Finalized, authoritative input transcript emitted when a speech turn finishes. |699700## Limitations701702- **Session duration:** Live transcription sessions support continuous streaming for up to 10 minutes.703- **Speaker diarization:** Speaker diarization is not supported in live streaming sessions. For speaker diarization, use the non-streaming [Audio transcription](https://ai.google.dev/gemini-api/docs/transcribe#speaker-diarization) endpoint.704- **Word-level timestamps:** Word-level timestamps are not supported over the Live API. The Live API emits utterance-level timestamps (`interim_input_transcription` and `input_transcription`).705- **Custom vocabulary:** You can provide up to 1,000 terms in `custom_vocabulary`, but best results are typically achieved with up to 100 terms.706- **Mode compatibility:** Smart transcription (`"mode": "SMART"`) removes filler words and formats intent-aware text, but cannot be combined with word annotations.707708## What's next709710- Read the [Gemini Transcribe documentation](https://ai.google.dev/gemini-api/docs/transcribe) for non-streaming audio files.711- Read the [Live API overview](https://ai.google.dev/gemini-api/docs/live-api) for conversational voice agents.712- Read the [Live translation guide](https://ai.google.dev/gemini-api/docs/live-api/live-translate) for real-time speech-to-speech translation.713- Check the [Pricing page](https://ai.google.dev/gemini-api/docs/pricing#gemini-3.5-transcribe-live) for Live API streaming pricing.714- Explore the [Live API capabilities guide](https://ai.google.dev/gemini-api/docs/live-api/capabilities).