# Groq vs OpenAI Whisper Integration: A Technical Comparison in claude-video

> Compare Groq vs OpenAI Whisper integration in claude-video. Understand the technical differences in backends, API keys, and Groq's unique requirements for seamless video transcription.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: technical-comparison
- Published: 2026-08-10

---

**The claude-video repository's `watch` skill supports both Groq and OpenAI Whisper backends with identical chunking, retry, and parsing logic, differing only in endpoint URLs, model identifiers, API key environment variables, and a custom User-Agent header required for Groq's Cloudflare edge.**

The `watch` skill in [bradautomates/claude-video](https://github.com/bradautomates/claude-video) provides flexible audio transcription by abstracting Whisper access behind a unified interface. Understanding the Groq vs OpenAI Whisper implementation differences helps developers optimize for latency, cost, and availability when processing video content.

## Endpoint and Model Configuration Differences

The primary distinction between backends lives in the `ENDPOINTS` dictionary defined at the top of [`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py) (lines 29-34):

```python
ENDPOINTS = {
    "groq": {
        "url": "https://api.groq.com/openai/v1/audio/transcriptions",
        "model": "whisper-large-v3",  # Largest Groq-hosted model

    },
    "openai": {
        "url": "https://api.openai.com/v1/audio/transcriptions",
        "model": "whisper-1",  # Default OpenAI model

    },
}

```

**Groq** routes to `whisper-large-v3` on Groq's LPU infrastructure, while **OpenAI** uses the standard `whisper-1` model. Both expose OpenAI-compatible HTTP endpoints, enabling the shared client implementation.

## API Key Resolution Priority

The `load_api_key()` function (lines 65-71 and 98-108) implements a preference-based fallback system:

1. **`GROQ_API_KEY`** — checked first regardless of which backend is requested
2. **`OPENAI_API_KEY`** — used as secondary fallback

This design allows seamless switching without manual key management. When running:

```bash
watch https://youtu.be/xyz

```

The skill auto-selects Groq if `GROQ_API_KEY` is present, otherwise falls back to OpenAI.

To force a specific backend from the CLI:

```bash
watch https://youtu.be/xyz --whisper openai

```

Store keys in `~/.config/watch/.env`:

```bash
GROQ_API_KEY="your-groq-key-here"
OPENAI_API_KEY="your-openai-key-here"

```

## Network-Level Differences: User-Agent Handling

The `_post_whisper()` function (lines 45-51) adds backend-specific headers:

```python
headers = {
    "Authorization": f"Bearer {api_key}",
    # Custom UA required for Groq's Cloudflare edge

    "User-Agent": "watch-skill/1.0 (+claude-code; python-urllib)",
}

```

**Groq's Cloudflare front-end blocks generic Python urllib User-Agents**, necessitating this custom identifier. The same header is sent to both services, but it only affects request success with Groq. OpenAI accepts the standard urllib identity without issue.

## Shared Implementation: Chunking, Retries, and Parsing

Beyond the four backend-specific differences (endpoint, model, API key variable, User-Agent), the codebase treats both services identically.

### Audio Preparation

The `extract_audio()` function generates a mono 16 kHz MP3 that satisfies both services' 25 MiB upload limit. No backend-specific encoding logic exists.

### Intelligent Chunking

`plan_chunks()` in `_post_whisper()` (line 58) calculates time-aligned splits using `MAX_UPLOAD_BYTES` when audio exceeds the limit. Both services receive identically-sized chunks.

### Retry Logic

HTTP 429 rate-limit responses trigger identical exponential backoff for both backends (lines 71-78):

```python
if resp.status == 429 and attempt < MAX_429_RETRIES:
    time.sleep(2 ** attempt)  # Exponential backoff

    continue

```

The `MAX_429_RETRIES` constant applies universally—no service-specific tuning is implemented.

### Response Normalization

`_segments_from_response()` (line 82) parses the JSON `segments` array into a unified format used throughout the pipeline:

```json
{"start": 0.0, "end": 5.32, "text": "Transcribed content..."}

```

This abstraction allows downstream components to remain backend-agnostic.

## Practical Usage Examples

Run transcription with auto-selected backend:

```bash
python3 -m skills.watch.scripts.whisper /path/to/video.mp4

```

Force OpenAI explicitly:

```bash
watch https://youtu.be/xyz --whisper openai

```

Use Groq via environment variable:

```bash
export GROQ_API_KEY="your-groq-key-here"
watch https://youtu.be/xyz

```

## Key Source Files

| File | Responsibility |
|------|---------------|
| [`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py) | Core transcription logic: endpoints, models, API keys, multipart upload, retries |
| [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py) | Entry point that orchestrates `transcribe_video()` and backend selection |
| [`skills/watch/scripts/config.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/config.py) | User configuration loading for `.env` file location |
| [`skills/watch/scripts/setup.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/setup.py) | Generates default `.env` templates for new users |

## Summary

- **Groq vs OpenAI Whisper integration** in claude-video differs in four specific areas: endpoint URL, model name (`whisper-large-v3` vs `whisper-1`), preferred API key environment variable, and a custom User-Agent header required for Groq's Cloudflare edge
- **All other logic is shared**: audio extraction, chunked upload planning, multipart request construction, rate-limit retry handling, and response segment parsing
- **API key resolution prefers Groq**: `GROQ_API_KEY` is checked before `OPENAI_API_KEY` regardless of requested backend
- **The custom User-Agent header** (`watch-skill/1.0 (+claude-code; python-urllib)`) exists solely to satisfy Groq's WAF rules and is harmless to OpenAI
- **Backend switching is transparent** at the CLI level via `--whisper groq|openai` with no code changes required

## Frequently Asked Questions

### How do I switch between Groq and OpenAI Whisper backends?

Use the `--whisper` flag with the `watch` command: `watch URL --whisper openai` forces OpenAI, while omitting the flag auto-selects based on available API keys. The `GROQ_API_KEY` environment variable takes precedence if present.

### Why does the Groq backend need a custom User-Agent header?

Groq's API sits behind Cloudflare's WAF, which blocks requests from generic `python-urllib` User-Agents. The `_post_whisper()` function in [`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py) (lines 45-51) sends `watch-skill/1.0 (+claude-code; python-urllib)` to bypass this restriction. OpenAI does not enforce this rule.

### Do both backends support the same audio formats and limits?

Yes. The `extract_audio()` function produces identical mono 16 kHz MP3 output for both services, and both respect the same 25 MiB per-request limit handled by `plan_chunks()`. The chunking, retry, and response parsing code is fully shared between backends.

### Which Whisper model does each backend use?

According to [`skills/watch/scripts/whisper.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/whisper.py) lines 30-33, Groq uses `whisper-large-v3` (its largest hosted model) while OpenAI uses `whisper-1` (the default API model). These are hardcoded in the `ENDPOINTS` dictionary and cannot be overridden without modifying the source.