Overview
This tutorial walks you through the complete process of uploading an audio file, getting it transcribed, and translated into multiple languages using the Tiro API.Prerequisites
- Valid Tiro API key
- Audio file (MP3, WAV, M4A)
- Max file size: 500MB
- Max duration: 4 hours
Step 1: Create a Voice File Job
Start by creating a job with your desired transcription and translation settings:curl -X POST https://api.tiro.ooo/v1/external/voice-file/jobs \
-H "Authorization: Bearer $TIRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transcriptLocaleHints": ["ko_KR"],
"translationLocales": ["en_US"]
}'
async function createVoiceFileJob() {
const response = await fetch('https://api.tiro.ooo/v1/external/voice-file/jobs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
transcriptLocaleHints: ['ko_KR'],
translationLocales: ['en_US']
})
});
if (!response.ok) {
throw new Error(`Failed to create job: ${response.status}`);
}
const job = await response.json();
console.log('Job created:', job.id);
console.log('Upload URL:', job.uploadUri);
return job;
}
import requests
import os
def create_voice_file_job():
response = requests.post(
'https://api.tiro.ooo/v1/external/voice-file/jobs',
headers={
'Authorization': f'Bearer {os.environ["TIRO_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'transcriptLocaleHints': ['ko_KR'],
'translationLocales': ['en_US']
}
)
response.raise_for_status()
job = response.json()
print(f'Job created: {job["id"]}')
print(f'Upload URL: {job["uploadUri"]}')
return job
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
type CreateJobRequest struct {
TranscriptLocaleHints []string `json:"transcriptLocaleHints"`
TranslationLocales []string `json:"translationLocales"`
}
type CreateJobResponse struct {
ID string `json:"id"`
UploadUri string `json:"uploadUri"`
}
func createVoiceFileJob() (*CreateJobResponse, error) {
reqBody := CreateJobRequest{
TranscriptLocaleHints: []string{"ko_KR"},
TranslationLocales: []string{"en_US"},
}
jsonBody, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("POST", "https://api.tiro.ooo/v1/external/voice-file/jobs", bytes.NewBuffer(jsonBody))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("TIRO_API_KEY")))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var job CreateJobResponse
json.NewDecoder(resp.Body).Decode(&job)
fmt.Printf("Job created: %s\n", job.ID)
fmt.Printf("Upload URL: %s\n", job.UploadUri)
return &job, nil
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.*
import org.springframework.web.client.RestTemplate
data class CreateJobRequest(
val transcriptLocaleHints: List<String>,
val translationLocales: List<String>
)
data class CreateJobResponse(
val id: String,
val uploadUri: String
)
@Service
class VoiceFileService {
@Value("\${tiro.api.key}")
private lateinit var apiKey: String
private val restTemplate = RestTemplate()
fun createVoiceFileJob(): CreateJobResponse {
val headers = HttpHeaders().apply {
set("Authorization", "Bearer $apiKey")
contentType = MediaType.APPLICATION_JSON
}
val request = CreateJobRequest(
transcriptLocaleHints = listOf("ko_KR"),
translationLocales = listOf("en_US")
)
val entity = HttpEntity(request, headers)
val response = restTemplate.exchange(
"https://api.tiro.ooo/v1/external/voice-file/jobs",
HttpMethod.POST,
entity,
CreateJobResponse::class.java
)
val job = response.body!!
println("Job created: ${job.id}")
println("Upload URL: ${job.uploadUri}")
return job
}
}
{
"id": "b2d1ab32-3fe2-4201-b0b4-391abdbaa023",
"uploadUri": "https://storage.example.com/upload/signed-url"
}
Step 2: Upload Your Audio File
Upload your audio file to the provided signed URL:# Upload audio file to the signed URL (replace UPLOAD_URI with the uploadUri from Step 1)
curl -X PUT "$UPLOAD_URI" \
-H "Content-Type: audio/mpeg" \
--data-binary @/path/to/your/audio.mp3
async function uploadAudioFile(uploadUri, audioFile) {
const response = await fetch(uploadUri, {
method: 'PUT',
body: audioFile,
headers: {
'Content-Type': audioFile.type || 'audio/mpeg'
}
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`);
}
console.log('File uploaded successfully');
}
// For Node.js with file system
const fs = require('fs');
async function uploadAudioFileNode(uploadUri, filePath) {
const fileBuffer = fs.readFileSync(filePath);
const response = await fetch(uploadUri, {
method: 'PUT',
body: fileBuffer,
headers: {
'Content-Type': 'audio/mpeg'
}
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`);
}
console.log('File uploaded successfully');
}
def upload_audio_file(upload_uri, file_path):
with open(file_path, 'rb') as audio_file:
response = requests.put(
upload_uri,
data=audio_file,
headers={'Content-Type': 'audio/mpeg'}
)
response.raise_for_status()
print('File uploaded successfully')
func uploadAudioFile(uploadUri, filePath string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
req, _ := http.NewRequest("PUT", uploadUri, file)
req.Header.Set("Content-Type", "audio/mpeg")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("upload failed: %d", resp.StatusCode)
}
fmt.Println("File uploaded successfully")
return nil
}
fun uploadAudioFile(uploadUri: String, filePath: String) {
val file = File(filePath)
val headers = HttpHeaders().apply {
contentType = MediaType.parseMediaType("audio/mpeg")
}
val resource = FileSystemResource(file)
val entity = HttpEntity(resource, headers)
val response = restTemplate.exchange(
uploadUri,
HttpMethod.PUT,
entity,
String::class.java
)
if (response.statusCode.is2xxSuccessful) {
println("File uploaded successfully")
} else {
throw RuntimeException("Upload failed: ${response.statusCode}")
}
}
Step 3: Notify Upload Completion
Tell the API that the upload is complete:curl -X PUT "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/upload-complete" \
-H "Authorization: Bearer $TIRO_API_KEY" \
-H "Content-Type: application/json"
async function notifyUploadComplete(jobId) {
const response = await fetch(
`https://api.tiro.ooo/v1/external/voice-file/jobs/${jobId}/upload-complete`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
if (!response.ok) {
throw new Error(`Failed to notify upload: ${response.status}`);
}
console.log('Upload notification sent');
}
def notify_upload_complete(job_id):
response = requests.put(
f'https://api.tiro.ooo/v1/external/voice-file/jobs/{job_id}/upload-complete',
headers={
'Authorization': f'Bearer {os.getenv("TIRO_API_KEY")}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
print('Upload notification sent')
func notifyUploadComplete(jobId string) error {
url := fmt.Sprintf("https://api.tiro.ooo/v1/external/voice-file/jobs/%s/upload-complete", jobId)
req, _ := http.NewRequest("PUT", url, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("TIRO_API_KEY")))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Println("Upload notification sent")
return nil
}
fun notifyUploadComplete(jobId: String) {
val headers = HttpHeaders().apply {
set("Authorization", "Bearer $apiKey")
contentType = MediaType.APPLICATION_JSON
}
val entity = HttpEntity<String>(headers)
restTemplate.exchange(
"https://api.tiro.ooo/v1/external/voice-file/jobs/$jobId/upload-complete",
HttpMethod.PUT,
entity,
String::class.java
)
println("Upload notification sent")
}
Step 4: Poll for Job Completion
Monitor the job status until processing is complete:# Check job status (poll until status is COMPLETED or FAILED)
curl -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID" \
-H "Authorization: Bearer $TIRO_API_KEY"
# Example response:
# {
# "id": "job_abc123",
# "status": "COMPLETED",
# "fileUploadedAt": "2024-01-01T12:00:20Z",
# "processStartedAt": "2024-01-01T12:01:00Z",
# "processCompletedAt": "2024-01-01T12:02:30Z"
# }
async function waitForJobCompletion(jobId) {
let delay = 2000; // Start with 2 seconds
const maxDelay = 30000; // Max 30 seconds
const maxAttempts = 60; // 30 minutes max
let attempts = 0;
while (attempts < maxAttempts) {
try {
const response = await fetch(
`https://api.tiro.ooo/v1/external/voice-file/jobs/${jobId}`,
{
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to get job status: ${response.status}`);
}
const job = await response.json();
console.log(`Job status: ${job.status}`);
if (job.status === 'COMPLETED') {
console.log('Job completed successfully!');
return job;
}
if (job.status === 'FAILED') {
throw new Error(`Job failed: ${job.errorMessage || 'Unknown error'}`);
}
// Still processing - wait with exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
delay = Math.min(delay * 1.2, maxDelay);
attempts++;
} catch (error) {
console.error('Error checking job status:', error);
attempts++;
await new Promise(resolve => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, maxDelay);
}
}
throw new Error('Job did not complete within timeout');
}
import time
import asyncio
async def wait_for_job_completion(job_id):
delay = 2 # Start with 2 seconds
max_delay = 30 # Max 30 seconds
max_attempts = 60 # 30 minutes max
attempts = 0
while attempts < max_attempts:
try:
response = requests.get(
f'https://api.tiro.ooo/v1/external/voice-file/jobs/{job_id}',
headers={
'Authorization': f'Bearer {os.getenv("TIRO_API_KEY")}'
}
)
response.raise_for_status()
job = response.json()
print(f'Job status: {job["status"]}')
if job['status'] == 'COMPLETED':
print('Job completed successfully!')
return job
if job['status'] == 'FAILED':
error_msg = job.get('errorMessage', 'Unknown error')
raise Exception(f'Job failed: {error_msg}')
# Still processing - wait with exponential backoff
await asyncio.sleep(delay)
delay = min(delay * 1.2, max_delay)
attempts += 1
except Exception as e:
print(f'Error checking job status: {e}')
attempts += 1
await asyncio.sleep(delay)
delay = min(delay * 1.5, max_delay)
raise Exception('Job did not complete within timeout')
type JobStatus struct {
ID string `json:"id"`
Status string `json:"status"`
ErrorMessage string `json:"errorMessage,omitempty"`
}
func waitForJobCompletion(jobId string) (*JobStatus, error) {
delay := 2 * time.Second
maxDelay := 30 * time.Second
maxAttempts := 60
client := &http.Client{}
url := fmt.Sprintf("https://api.tiro.ooo/v1/external/voice-file/jobs/%s", jobId)
for attempts := 0; attempts < maxAttempts; attempts++ {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("TIRO_API_KEY")))
resp, err := client.Do(req)
if err != nil {
time.Sleep(delay)
delay = time.Duration(float64(delay) * 1.5)
if delay > maxDelay {
delay = maxDelay
}
continue
}
var job JobStatus
json.NewDecoder(resp.Body).Decode(&job)
resp.Body.Close()
fmt.Printf("Job status: %s\n", job.Status)
if job.Status == "COMPLETED" {
fmt.Println("Job completed successfully!")
return &job, nil
}
if job.Status == "FAILED" {
return nil, fmt.Errorf("job failed: %s", job.ErrorMessage)
}
time.Sleep(delay)
delay = time.Duration(float64(delay) * 1.2)
if delay > maxDelay {
delay = maxDelay
}
}
return nil, fmt.Errorf("job did not complete within timeout")
}
import java.util.concurrent.TimeUnit
data class JobStatus(
val id: String,
val status: String,
val errorMessage: String? = null
)
fun waitForJobCompletion(jobId: String): JobStatus {
var delay = 2000L // 2 seconds
val maxDelay = 30000L // 30 seconds
val maxAttempts = 60
repeat(maxAttempts) { attempt ->
try {
val headers = HttpHeaders().apply {
set("Authorization", "Bearer $apiKey")
}
val entity = HttpEntity<String>(headers)
val response = restTemplate.exchange(
"https://api.tiro.ooo/v1/external/voice-file/jobs/$jobId",
HttpMethod.GET,
entity,
JobStatus::class.java
)
val job = response.body!!
println("Job status: ${job.status}")
when (job.status) {
"COMPLETED" -> {
println("Job completed successfully!")
return job
}
"FAILED" -> {
throw RuntimeException("Job failed: ${job.errorMessage}")
}
}
TimeUnit.MILLISECONDS.sleep(delay)
delay = minOf((delay * 1.2).toLong(), maxDelay)
} catch (e: Exception) {
println("Error checking job status: ${e.message}")
TimeUnit.MILLISECONDS.sleep(delay)
delay = minOf((delay * 1.5).toLong(), maxDelay)
}
}
throw RuntimeException("Job did not complete within timeout")
}
Step 5: Retrieve Results
Once the job is completed, fetch the transcript and translations:# Get transcript
curl -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/transcript" \
-H "Authorization: Bearer $TIRO_API_KEY"
# Get all translations
curl -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/translations" \
-H "Authorization: Bearer $TIRO_API_KEY"
# Get specific translation (e.g., English)
curl -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/translations/en_US" \
-H "Authorization: Bearer $TIRO_API_KEY"
# Get transcript paragraph summary
curl -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/transcript/paragraph-summary" \
-H "Authorization: Bearer $TIRO_API_KEY"
# Get translation paragraph summary
curl -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/translations/en_US/paragraph-summary" \
-H "Authorization: Bearer $TIRO_API_KEY"
async function getJobResults(jobId) {
// Get transcript
const transcriptResponse = await fetch(
`https://api.tiro.ooo/v1/external/voice-file/jobs/${jobId}/transcript`,
{
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`
}
}
);
if (!transcriptResponse.ok) {
throw new Error(`Failed to get transcript: ${transcriptResponse.status}`);
}
const transcript = await transcriptResponse.json();
console.log('Transcript:', transcript.text);
// Get available translations
const translationsResponse = await fetch(
`https://api.tiro.ooo/v1/external/voice-file/jobs/${jobId}/translations`,
{
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`
}
}
);
if (!translationsResponse.ok) {
throw new Error(`Failed to get translations: ${translationsResponse.status}`);
}
const translations = await translationsResponse.json();
// Get each translation
const translationResults = {};
for (const translation of translations) {
translationResults[translation.locale] = translation.text;
}
// Get paragraph summaries
const transcriptSummaryResponse = await fetch(
`https://api.tiro.ooo/v1/external/voice-file/jobs/${jobId}/transcript/paragraph-summary`,
{
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`
}
}
);
let transcriptSummary = null;
if (transcriptSummaryResponse.ok) {
transcriptSummary = await transcriptSummaryResponse.json();
}
// Get translation summaries
const translationSummaries = {};
for (const translation of translations) {
const summaryResponse = await fetch(
`https://api.tiro.ooo/v1/external/voice-file/jobs/${jobId}/translations/${translation.locale}/paragraph-summary`,
{
headers: {
'Authorization': `Bearer ${process.env.TIRO_API_KEY}`
}
}
);
if (summaryResponse.ok) {
const summaryData = await summaryResponse.json();
translationSummaries[translation.locale] = summaryData.summary;
}
}
return {
transcript: transcript.text,
translations: translationResults,
transcriptSummary: transcriptSummary?.summary,
translationSummaries
};
}
def get_job_results(job_id):
# Get transcript
transcript_response = requests.get(
f'https://api.tiro.ooo/v1/external/voice-file/jobs/{job_id}/transcript',
headers={'Authorization': f'Bearer {os.getenv("TIRO_API_KEY")}'}
)
transcript_response.raise_for_status()
transcript = transcript_response.json()
print(f'Transcript: {transcript["text"]}')
# Get available translations
translations_response = requests.get(
f'https://api.tiro.ooo/v1/external/voice-file/jobs/{job_id}/translations',
headers={'Authorization': f'Bearer {os.getenv("TIRO_API_KEY")}'}
)
translations_response.raise_for_status()
translations = translations_response.json()
# Get each translation
translation_results = {}
for translation in translations:
translation_results[translation['locale']] = translation['text']
# Get paragraph summaries
transcript_summary = None
try:
transcript_summary_response = requests.get(
f'https://api.tiro.ooo/v1/external/voice-file/jobs/{job_id}/transcript/paragraph-summary',
headers={'Authorization': f'Bearer {os.getenv("TIRO_API_KEY")}'}
)
if transcript_summary_response.ok:
transcript_summary_data = transcript_summary_response.json()
transcript_summary = transcript_summary_data['summary']
except:
pass
# Get translation summaries
translation_summaries = {}
for translation in translations:
try:
summary_response = requests.get(
f'https://api.tiro.ooo/v1/external/voice-file/jobs/{job_id}/translations/{translation["locale"]}/paragraph-summary',
headers={'Authorization': f'Bearer {os.getenv("TIRO_API_KEY")}'}
)
if summary_response.ok:
summary_data = summary_response.json()
translation_summaries[translation['locale']] = summary_data['summary']
except:
pass
return {
'transcript': transcript['text'],
'translations': translation_results,
'transcript_summary': transcript_summary,
'translation_summaries': translation_summaries
}
type Transcript struct {
JobId string `json:"jobId"`
Locales []string `json:"locales"`
Text string `json:"text"`
}
type Translation struct {
JobId string `json:"jobId"`
Locale string `json:"locale"`
Text string `json:"text"`
}
type JobResults struct {
Transcript string
Translations map[string]string
}
func getJobResults(jobId string) (*JobResults, error) {
client := &http.Client{}
apiKey := os.Getenv("TIRO_API_KEY")
// Get transcript
transcriptUrl := fmt.Sprintf("https://api.tiro.ooo/v1/external/voice-file/jobs/%s/transcript", jobId)
req, _ := http.NewRequest("GET", transcriptUrl, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var transcript Transcript
json.NewDecoder(resp.Body).Decode(&transcript)
fmt.Printf("Transcript: %s\n", transcript.Text)
// Get translations
translationsUrl := fmt.Sprintf("https://api.tiro.ooo/v1/external/voice-file/jobs/%s/translations", jobId)
req, _ = http.NewRequest("GET", translationsUrl, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
resp, err = client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var translations []Translation
json.NewDecoder(resp.Body).Decode(&translations)
translationResults := make(map[string]string)
for _, t := range translations {
translationResults[t.Locale] = t.Text
}
return &JobResults{
Transcript: transcript.Text,
Translations: translationResults,
}, nil
}
data class Transcript(
val jobId: String,
val locales: List<String>,
val text: String
)
data class Translation(
val jobId: String,
val locale: String,
val text: String
)
data class JobResults(
val transcript: String,
val translations: Map<String, String>
)
fun getJobResults(jobId: String): JobResults {
val headers = HttpHeaders().apply {
set("Authorization", "Bearer $apiKey")
}
val entity = HttpEntity<String>(headers)
// Get transcript
val transcriptResponse = restTemplate.exchange(
"https://api.tiro.ooo/v1/external/voice-file/jobs/$jobId/transcript",
HttpMethod.GET,
entity,
Transcript::class.java
)
val transcript = transcriptResponse.body!!
println("Transcript: ${transcript.text}")
// Get translations
val translationsResponse = restTemplate.exchange(
"https://api.tiro.ooo/v1/external/voice-file/jobs/$jobId/translations",
HttpMethod.GET,
entity,
object : ParameterizedTypeReference<List<Translation>>() {}
)
val translations = translationsResponse.body!!
val translationResults = translations.associate { it.locale to it.text }
return JobResults(
transcript = transcript.text,
translations = translationResults
)
}
Complete Example
Here’s a complete example that processes an audio file from start to finish:#!/bin/bash
# Complete Voice File Processing Script
# Set your API key
export TIRO_API_KEY="your_api_key_here"
AUDIO_FILE="/path/to/your/audio.mp3"
# Step 1: Create job
echo "Creating voice file job..."
RESPONSE=$(curl -s -X POST https://api.tiro.ooo/v1/external/voice-file/jobs \
-H "Authorization: Bearer $TIRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transcriptLocaleHints": ["ko_KR"],
"translationLocales": ["en_US"]
}')
JOB_ID=$(echo $RESPONSE | jq -r '.id')
UPLOAD_URI=$(echo $RESPONSE | jq -r '.uploadUri')
echo "Job created: $JOB_ID"
# Step 2: Upload file
echo "Uploading audio file..."
curl -s -X PUT "$UPLOAD_URI" \
-H "Content-Type: audio/mpeg" \
--data-binary @"$AUDIO_FILE"
echo "File uploaded successfully"
# Step 3: Notify upload complete
echo "Notifying upload completion..."
curl -s -X PUT "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/upload-complete" \
-H "Authorization: Bearer $TIRO_API_KEY" \
-H "Content-Type: application/json"
# Step 4: Poll for completion
echo "Waiting for processing to complete..."
while true; do
STATUS=$(curl -s -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID" \
-H "Authorization: Bearer $TIRO_API_KEY" | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" = "COMPLETED" ]; then
echo "Job completed successfully!"
break
elif [ "$STATUS" = "FAILED" ]; then
echo "Job failed!"
exit 1
fi
sleep 5
done
# Step 5: Get results
echo "Retrieving results..."
echo "=== Transcript ==="
curl -s -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/transcript" \
-H "Authorization: Bearer $TIRO_API_KEY" | jq '.text'
echo "=== Translation (en_US) ==="
curl -s -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/translations/en_US" \
-H "Authorization: Bearer $TIRO_API_KEY" | jq '.text'
echo "=== Paragraph Summary ==="
curl -s -X GET "https://api.tiro.ooo/v1/external/voice-file/jobs/$JOB_ID/transcript/paragraph-summary" \
-H "Authorization: Bearer $TIRO_API_KEY" | jq '.summary'
echo "Processing completed!"
async function processAudioFile(audioFile) {
try {
// Step 1: Create job
console.log('Creating voice file job...');
const job = await createVoiceFileJob();
// Step 2: Upload file
console.log('Uploading audio file...');
await uploadAudioFile(job.uploadUri, audioFile);
// Step 3: Notify upload complete
console.log('Notifying upload completion...');
await notifyUploadComplete(job.id);
// Step 4: Wait for processing
console.log('Waiting for processing to complete...');
const completedJob = await waitForJobCompletion(job.id);
// Step 5: Get results
console.log('Retrieving results...');
const results = await getJobResults(job.id);
console.log('Processing completed!');
console.log('Transcript:', results.transcript);
console.log('Translations:', results.translations);
console.log('Transcript Summary:', results.transcriptSummary);
console.log('Translation Summaries:', results.translationSummaries);
return results;
} catch (error) {
console.error('Error processing audio file:', error);
throw error;
}
}
// Usage
processAudioFile(myAudioFile)
.then(results => {
// Handle successful processing
displayResults(results);
})
.catch(error => {
// Handle error
showError(error.message);
});
async def process_audio_file(file_path):
try:
# Step 1: Create job
print('Creating voice file job...')
job = create_voice_file_job()
# Step 2: Upload file
print('Uploading audio file...')
upload_audio_file(job['uploadUri'], file_path)
# Step 3: Notify upload complete
print('Notifying upload completion...')
notify_upload_complete(job['id'])
# Step 4: Wait for processing
print('Waiting for processing to complete...')
completed_job = await wait_for_job_completion(job['id'])
# Step 5: Get results
print('Retrieving results...')
results = get_job_results(job['id'])
print('Processing completed!')
print(f'Transcript: {results["transcript"]}')
print(f'Translations: {results["translations"]}')
print(f'Transcript Summary: {results["transcript_summary"]}')
print(f'Translation Summaries: {results["translation_summaries"]}')
return results
except Exception as e:
print(f'Error processing audio file: {e}')
raise
# Usage
if __name__ == '__main__':
asyncio.run(process_audio_file('path/to/audio.mp3'))
Step 6: Understanding Paragraph Summaries
The Paragraph Summary feature provides intelligent content summarization that helps you quickly understand the key points from your audio files.What are Paragraph Summaries?
Paragraph summaries are automatically generated overviews of your transcript or translation content, broken down into logical sections. Each summary provides:- Concise Overview: Key points from each paragraph in markdown format
- Language-Specific: Summaries generated for both transcript and translations
- Structured Content: Easy to parse and integrate into your applications
Example Response
When you fetch paragraph summaries, you’ll receive a response like this:{
"jobId": "job-123",
"locale": "ko_KR",
"summary": [
{
"type": "markdown",
"content": "### 회의는 프로젝트 진행 상황을 점검하고, 다음 단계에 대한 계획을 수립하는 데 중점을 두었습니다."
},
{
"type": "markdown",
"content": "### 팀원들은 각자의 담당 업무에 대한 현재 상태를 보고하고, 발생한 이슈들에 대해 논의했습니다."
}
]
}
When are Summaries Available?
- Transcript and Translation Summaries: Available after job completion (
COMPLETEDstatus)COMPLETEDmeans all processing is finished: transcript, translation (if requested), and paragraph summaries for both
- Processing Time: Summaries are generated asynchronously, usually within 30-60 seconds after the main processing
Polling Best Practices
Exponential Backoff Strategy
- Start delay: 2 seconds (processing takes time to start)
- Growth factor: 1.2x (gentle increase)
- Maximum delay: 30 seconds (avoid overwhelming the server)
- Maximum attempts: Based on expected processing time
Job State Transitions
Processing Time Guidelines
| File Duration | Expected Processing Time | Recommended Poll Interval |
|---|---|---|
| < 5 minutes | 30-60 seconds | Start: 2s, Max: 10s |
| 5-30 minutes | 1-5 minutes | Start: 5s, Max: 20s |
| 30-120 minutes | 5-15 minutes | Start: 10s, Max: 30s |
| > 2 hours | 15-30 minutes | Start: 30s, Max: 60s |
Common Issues & Solutions
Upload Failures
- Issue: 413 Payload Too Large
- Solution: Check file size (max 500MB) and compress if needed
Processing Failures
- Issue: Low audio quality
- Solution: Ensure sample rate ≥8kHz and minimal background noise
Timeout Issues
- Issue: Job doesn’t complete within expected time
- Solution: Increase timeout for longer files, check job state for errors