Why the Whisper Integration Uses Custom User-Agent Headers in Claude-Video

The Whisper integration in bradautomates/claude-video injects a custom User-Agent header to bypass Cloudflare's Web Application Firewall (WAF) rule 1010, which blocks the default Python-urllib/3.x user-agent with a 403 error before authentication occurs.

The claude-video repository implements a watch skill that transcribes video audio using external Whisper APIs. When contacting Groq's transcription endpoint, the code must present a non-default user-agent string to avoid being blocked by Cloudflare's protective filters. This defensive measure ensures reliable API communication while honestly identifying the client as the watch skill.

The Cloudflare WAF Blocking Problem

Groq's Whisper endpoint sits behind Cloudflare's Web Application Firewall (WAF), which aggressively filters requests based on user-agent signatures. When Python's standard urllib library sends requests, it uses the default header Python-urllib/3.x.

Cloudflare's WAF rule 1010 specifically targets and blocks this default user-agent, returning a 403 Forbidden response before the request ever reaches Groq's authentication layer. This means even valid API keys cannot authenticate because the connection is terminated at the edge.

The issue is documented in a detailed comment within the source code explaining that any non-default user-agent clears the block, allowing the Authorization header to be processed normally.

How the Custom User-Agent Header Works

The implementation deliberately crafts a recognizable user-agent string that satisfies Cloudflare's requirements while maintaining transparency about the client identity. In skills/watch/scripts/whisper.py at lines 44-51, the _post_whisper function constructs headers as follows:

headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": f"multipart/form-data; boundary={boundary}",
    # Groq sits behind Cloudflare — the default `Python-urllib/3.x` UA

    # trips WAF rule 1010 (403) before auth even runs. Any non‑default

    # UA clears it; we identify honestly.

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

This header identifies the client as watch-skill/1.0 with a reference to claude-code, distinguishing it from generic Python scripts. By supplying this custom value, the request passes Cloudflare's inspection and proceeds to Groq's API for transcription processing.

Implementation in the Codebase

The custom user-agent logic resides in the core Whisper client implementation, which handles both Groq and OpenAI endpoints.

The _post_whisper Function

Located in skills/watch/scripts/whisper.py, the _post_whisper function builds the multipart form data and injects the critical headers before sending the HTTP request via urllib:

def _post_whisper(endpoint: str, api_key: str, model: str, audio_path: Path) -> dict:
    fields = {
        "model": model,
        "response_format": "verbose_json",
        "temperature": "0",
    }
    body, boundary = _build_multipart(fields, audio_path)
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": f"multipart/form-data; boundary={boundary}",
        "User-Agent": "watch-skill/1.0 (+claude-code; python-urllib)",
    }
    # ... request sent via urllib ...

The function prepares the audio file for upload alongside parameters specifying the model and output format. The custom User-Agent is hardcoded to ensure every request to Groq's Cloudflare-protected endpoint avoids the 403 block.

Running the Integration

When executing the transcription workflow, the script automatically includes the custom header. Assuming a valid GROQ_API_KEY environment variable is configured (as referenced in setup.py), invoke the skill:

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

The entry point in skills/watch/scripts/watch.py orchestrates the full pipeline: downloading the video, extracting audio, and calling the Whisper client. Throughout this process, the custom user-agent ensures the Groq API remains accessible without manual intervention.

Summary

  • Cloudflare WAF rule 1010 blocks the default Python-urllib/3.x user-agent with a 403 error before authentication occurs.
  • The integration uses the custom header "watch-skill/1.0 (+claude-code; python-urllib)" to bypass this restriction.
  • The implementation resides in skills/watch/scripts/whisper.py within the _post_whisper function at lines 44-51.
  • This defensive measure applies specifically to the Groq endpoint, while maintaining compatibility with OpenAI's Whisper API.
  • The header provides honest identification for request tracing while ensuring reliable transcription service access.

Frequently Asked Questions

What is Cloudflare WAF rule 1010?

Cloudflare WAF rule 1010 is a security filter that blocks requests originating from generic automation tools and default library user-agents. In the context of the claude-video repository, this rule specifically targets the Python-urllib/3.x signature used by Python's standard HTTP client, preventing it from reaching Groq's authentication servers.

Does this affect OpenAI's Whisper API?

No, the custom user-agent primarily addresses Groq's Cloudflare-protected endpoint. While the _post_whisper function in whisper.py supports both Groq and OpenAI transcription services, the WAF blocking issue is specific to Groq's infrastructure. The custom header is applied universally to maintain consistency, but it is technically required only for the Groq integration.

What User-Agent string does the integration use?

The integration uses "watch-skill/1.0 (+claude-code; python-urllib)". This string identifies the client as version 1.0 of the watch skill, references the claude-code environment, and acknowledges the underlying Python urllib library. According to the source code comments in whisper.py, any non-default user-agent would satisfy Cloudflare's requirements, but this specific string provides transparent identification.

Where is the custom header configured in the codebase?

The custom User-Agent header is configured in skills/watch/scripts/whisper.py within the _post_whisper function at lines 44-51. This function constructs the HTTP headers dictionary that gets passed to the urllib request, including the Authorization, Content-Type, and the custom User-Agent required to bypass Cloudflare's WAF rules.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →