# How the Whisper API Integration Handles Files That Exceed the 25MB Limit

> Learn how the Whisper API integration seamlessly handles audio files over 25MB by splitting, transcribing, and reconstructing them for accurate transcriptions.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: how-to-guide
- Published: 2026-08-08

---

**The Whisper API integration automatically splits oversized audio files into chunks under 24 MiB using FFmpeg, transcribes each segment separately via Groq or OpenAI endpoints, and reconstructs the full transcript with adjusted timestamps to maintain synchronization with the original video.**

The `bradautomates/claude-video` repository implements a robust audio processing pipeline that respects the Whisper API's strict file size constraints. When extracted audio exceeds the 25MB upload threshold, the integration employs a pure-stdlib Python approach to chunk, transcribe, and reassemble segments without re-encoding the source media.

## Understanding the 25MB Upload Ceiling and Safety Margin

Both Groq and OpenAI Whisper services enforce a roughly 25 MiB upload limit per request. In [`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py), the code defines `MAX_UPLOAD_BYTES = 24 * 1024 * 1024` at line 37 to maintain a 1 MiB safety margin. This conservative threshold prevents edge-case rejections due to HTTP overhead or payload wrapping, ensuring reliable uploads even with minor byte-counting variations between client and server.

## The Six-Step Audio Chunking Pipeline

When the `transcribe_video` function detects oversized audio, it triggers a sophisticated segmentation workflow that preserves audio quality while respecting API limits.

### 1. Detecting Oversized Files

The entry point checks file size at lines 44-55. If `audio_bytes <= MAX_UPLOAD_BYTES`, the file uploads directly to the Whisper endpoint. Otherwise, the flow proceeds to the chunking logic to break the audio into compliant segments.

### 2. Planning Chunk Boundaries

The `plan_chunks` function (lines 40-62) calculates a list of `(offset, duration)` tuples using linear scaling based on constant-bitrate MP3 characteristics. This produces a segmentation plan where each chunk stays under the 24 MiB limit while distributing duration proportionally across the file.

### 3. Splitting Audio with FFmpeg

The `split_audio` function (lines 65-99) executes single-pass FFmpeg commands with `-c copy` for each planned slice. This extracts segments without re-encoding, preserving audio fidelity and processing speed. Chunks are written to a temporary subdirectory (`audio_out.parent / "chunks"`) to avoid cluttering the working directory.

### 4. Transcribing Individual Chunks

The `transcribe_chunks` helper (lines 71-81) iterates through chunk files, calling the Whisper endpoint for each segment. It implements per-chunk exception handling, catching failures and logging them without aborting the entire batch.

### 5. Adjusting Timestamps with shift_segments

Since each chunk starts at 0 seconds relative to its own file, the `shift_segments` function (lines 32-47) adds the original temporal offset to every segment timestamp. This ensures the final concatenated transcript reflects the actual video timeline rather than resetting at each chunk boundary.

### 6. Aggregating Results

The system extends the master segment list with each chunk's shifted results. If **every chunk fails**, the function raises a clear `Whisper failed on every audio chunk` error. Otherwise, it returns the aggregated segments to the caller in [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py).

## Implementation Examples

### Example 1: Standard Transcription (Under Limit)

```python
from pathlib import Path
from skills.watch.scripts.whisper import transcribe_video

video_path = "sample.mp4"
audio_out = Path("audio.mp3")
segments, backend = transcribe_video(video_path, audio_out)

print(f"Used {backend} backend, got {len(segments)} segments")

```

### Example 2: Automatic Chunking (Over Limit)

```python
from pathlib import Path
from skills.watch.scripts.whisper import transcribe_video

video_path = "big_video.mp4"  # Generates ~50 MiB audio

audio_out = Path("audio.mp3")
segments, backend = transcribe_video(video_path, audio_out)

# Console output shows chunking progress:

# [watch] audio: 50 MB exceeds 24 MB — splitting into 3 chunks…

# [watch] chunk 1/3 → 120 segments

# [watch] chunk 2/3 → 115 segments

# [watch] chunk 3/3 → 118 segments

print(f"Full transcript contains {len(segments)} segments")

```

### Example 3: Handling Partial Failures

```python

# If chunk 2 fails due to network timeout:

# [watch] chunk 2/3 failed — skipping (SystemExit: …)

# The function returns concatenated results of chunks 1 and 3 only,

# preserving partial progress rather than failing entirely

```

## Error Handling and Resilience

The pipeline implements defensive design patterns suitable for long-running video processing. Per-chunk errors are logged and skipped rather than aborting the entire job, preventing temporary network glitches from invalidating hours of processing on large files. However, total failure protection ensures users receive explicit feedback when audio is completely corrupted or inaccessible.

## Summary

- The integration reserves a **1 MiB safety margin**, using 24 MiB as the effective limit instead of the full 25 MiB.
- **FFmpeg `-c copy`** splits audio without quality loss or CPU-intensive re-encoding.
- The **`plan_chunks`** algorithm uses linear scaling to calculate precise byte-boundaries for constant-bitrate MP3s.
- **`shift_segments`** ensures reconstructed transcripts maintain accurate synchronization with the original video timeline.
- Partial failures are tolerated and logged; total failure raises explicit exceptions to prevent silent data loss.

## Frequently Asked Questions

### Why does the code use 24 MiB instead of the full 25 MiB limit?

The 1 MiB buffer accounts for HTTP headers, JSON payload wrapping, and potential byte-counting discrepancies between client and server implementations. This conservative approach, defined at line 37 of [`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py), prevents edge-case rejections at the API gateway while remaining well within the documented 25MB ceiling.

### Does chunking affect transcription accuracy or timestamp precision?

No. Because `split_audio` uses FFmpeg's `-c copy` flag, the audio codec remains bit-for-bit identical to the original. The Whisper API receives identical acoustic data, just segmented temporally. The `shift_segments` function (lines 32-47) then adds the chunk's temporal offset to every word-level timestamp, ensuring the final transcript aligns perfectly with the original video timeline.

### What happens if the network fails during processing of one chunk?

The `transcribe_chunks` function catches per-chunk exceptions and logs them with the pattern `[watch] chunk X/Y failed — skipping`. The final transcript contains only successfully transcribed segments. This resilience prevents a single network glitch from invalidating the entire job, though users should verify completeness if warnings appear in the logs.

### Is video re-encoding required to handle large files?

No. Only the extracted audio track undergoes chunking via FFmpeg. The original video file remains untouched throughout the process, minimizing CPU usage, preserving video quality, and reducing overall processing time compared to solutions that re-encode the entire media file.