How Whisper API Keys Are Loaded From Environment Variables vs Config Files in claude-video

The claude-video repository loads Whisper API keys by first checking environment variables (GROQ_API_KEY or OPENAI_API_KEY), then falling back to a user-specific ~/.config/watch/.env config file if the environment variables are unset.

The watch skill implements a two-stage loading mechanism that prioritizes environment variables for CI/CD and containerized deployments while supporting a local config file for interactive development. This design keeps secrets out of version control without sacrificing developer convenience.

Backend Selection Drives Key Loading

The loading process begins with backend selection. In skills/watch/scripts/watch.py (lines 239-257), the load_api_key() function determines which provider to use based on the --whisper CLI flag:

  • Groq — cheaper inference, preferred default
  • OpenAI — fallback when Groq is unavailable

If no flag is provided, the code attempts Groq first and automatically falls back to OpenAI when a Groq key cannot be found.

Two-Tier Key Resolution: Environment Variables First

The load_api_key() function in skills/watch/scripts/whisper.py implements ordered precedence:

Backend Environment Variable Config File Key
Groq GROQ_API_KEY groq_api_key
OpenAI OPENAI_API_KEY openai_api_key

Step 1: Environment Variable Check


# From skills/watch/scripts/whisper.py

import os

def load_api_key(preferred_backend=None):
    # Determine which backend to use

    backend = preferred_backend or "groq"
    
    # Map backend to environment variable name

    env_var_map = {
        "groq": "GROQ_API_KEY",
        "openai": "OPENAI_API_KEY"
    }
    
    # First: check environment variable

    api_key = os.getenv(env_var_map[backend])
    if api_key:
        return backend, api_key
    
    # Fall through to config file...

Step 2: Config File Fallback

When the environment variable is absent, the function loads ~/.config/watch/.env using python-dotenv:


# From skills/watch/scripts/whisper.py (continued)

from pathlib import Path
from dotenv import dotenv_values

def _load_user_config():
    """Load user-specific configuration from ~/.config/watch/.env"""
    config_dir = Path.home() / ".config" / "watch"
    config_dir.mkdir(parents=True, exist_ok=True)
    
    env_path = config_dir / ".env"
    return dotenv_values(env_path) if env_path.exists() else {}

def load_api_key(preferred_backend=None):
    # ... environment variable check above ...

    
    # Second: check config file

    config = _load_user_config()
    config_key_map = {
        "groq": "groq_api_key",
        "openai": "openai_api_key"
    }
    
    api_key = config.get(config_key_map[backend])
    if api_key:
        return backend, api_key
    
    # No key found — return None to trigger error handling

    return None, None

The config directory path is centralized in skills/watch/scripts/config.py to ensure consistency across the codebase.

Error Handling When Both Sources Fail

The caller in skills/watch/scripts/watch.py handles missing keys gracefully:


# From skills/watch/scripts/watch.py

from whisper import load_api_key, transcribe_video

backend, api_key = load_api_key(args.whisper)

if not api_key:
    print(
        f"[watch] whisper fallback failed: missing API key for {backend}",
        file=sys.stderr,
    )
    sys.exit(1)

# Proceed with transcription

transcript = transcribe_video(video_path, backend, api_key)

Practical Configuration Examples

Environment Variable Setup (Production/CI)


# Export for current session

export GROQ_API_KEY="gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Or use a .env file loaded by your orchestrator

docker run -e GROQ_API_KEY="$GROQ_API_KEY" claude-video watch "https://youtu.be/..."

Config File Setup (Local Development)


# Create directory with restricted permissions

mkdir -p ~/.config/watch
chmod 700 ~/.config/watch

# ~/.config/watch/.env — keep this file private (chmod 600)

groq_api_key=gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
openai_api_key=sk-openai-yyyyyyyyyyyyyyyyyyyyyyyyyyyy
chmod 600 ~/.config/watch/.env

Using the CLI With Explicit Backend


# Use default (Groq with fallback to OpenAI)

watch "https://youtu.be/dQw4w9WgXcQ"

# Force OpenAI Whisper

watch "https://youtu.be/dQw4w9WgXcQ" --whisper openai

# Force Groq (no fallback)

watch "https://youtu.be/dQw4w9WgXcQ" --whisper groq

Key Source Files

File Purpose
skills/watch/scripts/whisper.py Implements load_api_key() and backend-specific transcription logic
skills/watch/scripts/watch.py CLI entry point that calls load_api_key() and handles errors
skills/watch/scripts/config.py Centralizes paths including ~/.config/watch directory
tests/test_whisper.py Validates key loading, chunking, and error scenarios

Summary

  • Environment variables take precedence — set GROQ_API_KEY or OPENAI_API_KEY for deployment scenarios
  • Config file provides convenience — store keys in ~/.config/watch/.env for local development
  • Backend selection is flexible — use --whisper flag or accept automatic Groq→OpenAI fallback
  • Graceful degradation — missing keys produce clear error messages rather than cryptic failures
  • Security-conscious defaults — config directory creation with proper permissions, no keys in repository

Frequently Asked Questions

What happens if both GROQ_API_KEY and OPENAI_API_KEY are set?

The code uses backend selection logic first, not key availability. If you don't specify --whisper, it attempts Groq (GROQ_API_KEY). If you pass --whisper openai, it uses OPENAI_API_KEY regardless of whether Groq credentials exist.

Can I use a custom config file location?

Not directly through the current implementation. The path ~/.config/watch/.env is hardcoded in skills/watch/scripts/config.py. To override, set the appropriate environment variable instead, which always takes precedence.

Why does the code prefer Groq over OpenAI?

According to source comments in watch.py, Groq offers significantly cheaper inference for Whisper transcription. The automatic fallback to OpenAI ensures functionality when Groq rate limits or availability issues occur.

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 →