A FastAPI backend that transcribes interview audio using Groq Whisper Large v3 — no GPU, no local model download. Upload a pre-recorded file or record directly from your browser and get back a timestamped JSON transcript in seconds.
Audio File / Browser Recording
│
▼
┌─────────────┐
│ Compress │ → mono 16kHz 32kbps MP3 (shrinks files ~70%)
└─────────────┘
│
▼ if file > 20 MB
┌─────────────┐
│ Chunk │ → splits into 10-minute pieces
└─────────────┘
│
▼
┌─────────────────────────┐
│ Groq Whisper Large v3 │ → transcribes each chunk
└─────────────────────────┘
│
▼
┌─────────────┐
│ Merge │ → stitches segments with corrected timestamps
└─────────────┘
│
▼
Structured JSON transcript → saved to transcripts/
interview-transcriber/
├── main.py ← FastAPI app — routes, compression, chunking, transcription
├── config.py ← Loads GROQ_API_KEY from .env
├── pyproject.toml ← uv project config + dependencies
├── static/
│ └── recorder.html ← Browser-based PWA recorder (no install needed)
├── uploads/ ← Uploaded audio files (auto-created)
├── transcripts/ ← JSON transcripts (auto-created)
├── chunks/ ← Temp chunk files during processing (auto-cleaned)
└── .env ← Your API key (you create this)
| Requirement | Notes |
|---|---|
| Python ≥ 3.11 | Required |
| uv | Fast Python package manager |
| ffmpeg | Required by pydub for audio processing |
| Groq API key | Free at console.groq.com |
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
winget install astral-sh.uv# macOS
brew install ffmpeg
# Ubuntu / Debian
sudo apt install ffmpeg
# Windows (with Chocolatey)
choco install ffmpeggit clone <your-repo-url>
cd interview-transcriber
uv syncuv sync reads pyproject.toml, creates a virtual environment, and installs all dependencies automatically.
Create a .env file in the project root:
# .env
GROQ_API_KEY=gsk_your_key_hereGet your free key from console.groq.com.
config.pywill raise aRuntimeErrorat startup ifGROQ_API_KEYis missing or empty.
uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000| URL | Description |
|---|---|
http://localhost:8000 |
Base URL |
http://localhost:8000/docs |
Interactive Swagger UI |
http://localhost:8000/recorder |
Browser-based audio recorder |
Open http://localhost:8000/recorder on any device (desktop or mobile). No app install required.
Flow:
- Tap Start — grants mic access and begins recording
- A live timer and animated waveform show recording is active
- Tap Stop — audio is uploaded and transcribed automatically
- The Done screen displays duration, segment count, detected language, and interview ID
- Tap New Recording to start again
The recorder auto-selects the best supported audio format (webm/opus → ogg → mp4) and uploads directly to /transcribe.
Check server status.
curl http://localhost:8000/health{ "status": "ok", "transcription_engine": "groq/whisper-large-v3" }Upload an audio file and receive a structured JSON transcript.
Supported formats: .mp3 .wav .m4a .ogg .webm .flac .mp4
| Query Parameter | Type | Required | Description |
|---|---|---|---|
file |
file (form) | ✅ | The audio file |
interview_id |
string | ❌ | Custom ID — auto-generated (interview_<8hex>) if omitted |
language |
string | ❌ | ISO 639-1 code e.g. en, hi, es — auto-detected if omitted |
curl:
curl -X POST "http://localhost:8000/transcribe?language=en" \
-F "file=@interview.mp3"Python:
import requests
with open("interview.mp3", "rb") as f:
r = requests.post(
"http://localhost:8000/transcribe",
params={"interview_id": "candidate_bob", "language": "en"},
files={"file": ("interview.mp3", f, "audio/mpeg")},
)
print(r.json())Response:
{
"interview_id": "candidate_bob",
"original_file": "interview.mp3",
"transcribed_at": "2026-03-10T09:00:00",
"language": "en",
"duration_seconds": 182.4,
"total_segments": 24,
"full_transcript": "Tell me about yourself. Sure, I have five years of...",
"segments": [
{ "segment_id": 1, "start": 0.0, "end": 4.2, "text": "Tell me about yourself." },
{ "segment_id": 2, "start": 5.1, "end": 12.8, "text": "Sure, I have five years of experience in..." }
]
}Transcripts are saved automatically to transcripts/<interview_id>.json.
List all saved transcripts (summaries only, no segments).
curl http://localhost:8000/transcripts{
"total": 2,
"transcripts": [
{
"interview_id": "candidate_bob",
"original_file": "interview.mp3",
"transcribed_at": "2026-03-10T09:00:00",
"language": "en",
"duration_seconds": 182.4,
"total_segments": 24
}
]
}Retrieve a full transcript including all segments.
curl http://localhost:8000/transcripts/candidate_bobDelete a transcript permanently.
curl -X DELETE http://localhost:8000/transcripts/candidate_bob{ "deleted": "candidate_bob" }All tunable constants are at the top of main.py:
| Constant | Default | Description |
|---|---|---|
GROQ_MAX_BYTES |
20 * 1024 * 1024 (20 MB) |
Files larger than this are split into chunks before sending to Groq |
CHUNK_MINUTES |
10 |
Length of each audio chunk in minutes |
ALLOWED_EXTENSIONS |
.mp3 .mp4 .wav .m4a .ogg .webm .flac |
Accepted file types |
Audio compression settings (inside compress_audio()):
| Setting | Value | Rationale |
|---|---|---|
| Channels | Mono | Interview audio is mono; halves file size |
| Sample rate | 16 kHz | Sufficient for speech recognition |
| Bitrate | 32 kbps | Keeps files well under Groq's 25 MB limit |
Defined in pyproject.toml:
| Package | Purpose |
|---|---|
fastapi |
Web framework |
uvicorn[standard] |
ASGI server |
python-multipart |
Multipart form / file uploads |
groq |
Groq SDK — calls Whisper Large v3 |
pydub |
Audio compression and chunking (requires ffmpeg) |
python-dotenv |
Loads .env into environment variables |
| Limit | Value |
|---|---|
| Audio transcription | 2 hours / day |
| Transcription speed | ~10 s for a 30-minute interview |
| API key | Free at console.groq.com |
To use the browser recorder from a phone on a different network:
ngrok http 8000Open the forwarding URL on your phone:
https://xxxx.ngrok-free.app/recorder
| Problem | Fix |
|---|---|
RuntimeError: GROQ_API_KEY is not set |
Create .env with your key — see Setup step 4 |
pydub.exceptions.CouldntDecodeError |
Install ffmpeg — see Setup step 2 |
502 Transcription failed |
Check Groq API key validity and daily quota at console.groq.com |
| Microphone denied in browser | Allow mic permissions in browser settings; HTTPS required on mobile |
| Large file times out | File is being chunked — wait; each 10-min chunk takes ~3 s to transcribe |