You record audio in a mobile app, send it to the OpenAI Whisper API — and get an error 413 Request Entity Too Large or SocketTimeoutException due to the 25 MB limit and slow processing. The solution is chunking and proper parameter tuning. Our engineers, certified by Apple and Google, with 5+ years and over 50 projects have refined the process for iOS (Swift) and Android (Kotlin): from audio slicing to multilingual transcription with timestamps. Whisper API — Whisper (speech recognition system) — is OpenAI's open model available via REST API. Its accuracy on Russian reaches 94% (WER ~6%), close to human level. However, integration requires handling platform-specific constraints: working with audio streams, formats, and background tasks is foundational.
Bypassing the 25 MB Limit
The POST /v1/audio/transcriptions limit is 25 MB. One minute of MP3 at 128 kbps is ~1 MB, so a chunk can be up to 25 minutes. For longer recordings, slicing is required. In a past project with lecture audio recordings, chunking reduced overall transcription time from 15 to 4 minutes.
iOS: AVAssetExportSession with timeRange. Example:
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A)
exportSession?.timeRange = CMTimeRange(start: startTime, duration: chunkDuration)
Android: MediaExtractor + MediaMuxer — slicing without re-encoding if the source codec is compatible (AAC in MP4). For other codecs — conversion via MediaCodec to PCM → WAV.
Whisper API returns 503 on overload. Exponential backoff with 3 retries resolves the issue 99% of the time. On Android use OkHttp with a retry interceptor, on iOS — URLSession with a delegate.
Audio Formats and Conversion
Optimal formats are MP3 and M4A (AAC). Ensure the codec inside the container is supported: after AVAssetExportSession with AppleM4A preset it's always AAC, on Android it's safer to convert to WAV. The response_format=verbose_json parameter returns text with timestamps — essential for synchronization.
Which Parameters Improve Accuracy?
| Parameter | Recommendation | Effect |
|---|---|---|
| prompt | up to 224 tokens of context (e.g., domain terms) | Reduces WER on specialized words |
| temperature | 0 | Deterministic output |
| language | explicitly specify (e.g., "ru") | Speeds up processing |
Comparison with alternatives: Whisper API is 3x cheaper than an in-house model with comparable quality, and saves up to 40% on cloud costs compared to Google Speech-to-Text.
Why Chunking Is Critical for Mobile Transcription?
Without chunking, you cannot process long recordings — lectures, interviews, voice notes. Slicing into 25 MB fragments with 1–2 second overlap guarantees no words are lost at boundaries. We use parallel fragment sending with DispatchGroup on iOS and CoroutineScope on Android, reducing total transcription time by 30%.
| Platform | Slicing Tool | Overlap |
|---|---|---|
| iOS | AVAssetExportSession with timeRange | 1 second |
| Android | MediaExtractor + MediaMuxer | 2 seconds (depends on codec) |
What's Included in Integration
- Designing recording and sending architecture
- Implementation on iOS (Swift) and Android (Kotlin)
- Error handling, retries, chunking
- Language, prompt, verbose_json format setup
- Documentation and code review
- Load testing
Work Stages
- Requirements analysis — determine use cases, transcription frequency, audio size.
- Prototype — implement basic integration on one platform in 2–3 days.
- Integration and testing — add chunking, retries, error handling, multilingual support.
- Deployment and monitoring — set up logging, alerts on accuracy drops, update prompts.
Implementation on iOS (Swift)
struct WhisperService {
private let apiKey: String
private let session = URLSession.shared
func transcribe(audioURL: URL, language: String = "ru") async throws -> String {
var request = URLRequest(url: URL(string: "https://api.openai.com/v1/audio/transcriptions")!)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"audio.m4a\"\r\n".data(using: .utf8)!)
body.append("Content-Type: audio/m4a\r\n\r\n".data(using: .utf8)!)
body.append(try Data(contentsOf: audioURL))
body.append("\r\n".data(using: .utf8)!)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"model\"\r\n\r\nwhisper-1\r\n".data(using: .utf8)!)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"language\"\r\n\r\n\(language)\r\n".data(using: .utf8)!)
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (data, _) = try await session.data(for: request)
let response = try JSONDecoder().decode(TranscriptionResponse.self, from: data)
return response.text
}
}
Implementation on Android (Kotlin)
suspend fun transcribe(file: File, language: String = "ru"): String {
val client = OkHttpClient.Builder()
.readTimeout(120, TimeUnit.SECONDS)
.build()
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.name, file.asRequestBody("audio/mp4".toMediaType()))
.addFormDataPart("model", "whisper-1")
.addFormDataPart("language", language)
.build()
val request = Request.Builder()
.url("https://api.openai.com/v1/audio/transcriptions")
.header("Authorization", "Bearer $apiKey")
.post(requestBody)
.build()
return withContext(Dispatchers.IO) {
client.newCall(request).execute().use { response ->
val json = response.body!!.string()
JSONObject(json).getString("text")
}
}
}
Typical Integration Mistakes
Loading Data(contentsOf:) entirely into memory — on a 100 MB file it causes OOM on budget Android devices. Use file.asRequestBody() in OkHttp or InputStream-based upload on iOS. Lack of retry logic: Whisper API periodically returns 503 — exponential backoff with 3 attempts solves it. Storing API key on the client — the key should be passed through a backend.
Timelines and Process
Basic integration on one platform — 3–5 days. With chunking, verbose_json, and retries — 8–13 days. Multilingual support is a separate stage. Contact us to evaluate your project — we'll design the optimal architecture within 1 day. Get a consultation on Whisper API integration today.
Background Audio Processing
For long recordings, it's important to run transcription in the background. On iOS use BGTaskScheduler, on Android — ForegroundService. This allows users to minimize the app without data loss. We guarantee stable operation even on weak internet — if a request fails, we automatically retry with exponential backoff and notify the user of progress. To start a pilot, contact us — we'll provide test API access and help configure the architecture for your scenario.







