Build a Personal Meeting Notetaker with Groq's Free Whisper & Llama 3
What We're Building
We're building a local Python application that turns your meeting audio into a clean, structured summary with action items. No cloud dependencies beyond Groq's free API tier. No per-minute charges. No proprietary services that lock you in.
Here's what it does:
- Records system audio (your microphone, or loopback if you're capturing a call)
- Transcribes the audio to text using Groq's free Whisper endpoint
- Extracts a meeting summary, key decisions, and action items using Groq's free Llama 3 endpoint
- Outputs a clean markdown file you can paste into Notion, Linear, or Slack
We're using Groq because their free tier is genuinely generous: 1,000 requests/day for Whisper, and a high rate limit for Llama 3 inference. No credit card required. The entire stack runs on your laptop.
If you've already explored building an n8n-based meeting pipeline with local Whisper.cpp, check out our Build a Personal Meeting Notetaker That Transcribes Calls and Extracts Action Items guide for a no-code alternative. This time we're going pure Python for maximum control.
Architecture: Audio to Action Items
Here's the data flow. No magic—just three discrete stages chained together.
The critical insight: we keep the audio buffer in memory as a WAV byte stream, send it directly to Groq's Whisper endpoint, and feed the returned text straight into Llama 3 with a structured prompt. No disk I/O unless you want to save the raw audio for later.
Prerequisites and Free-Tier Setup
Everything here is free. No asterisks.
| Tool | Purpose | Free Tier | Setup Link |
|---|---|---|---|
| Python 3.10+ | Runtime | Always free | https://python.org |
| PyAudio | Audio capture | Always free | pip install pyaudio |
| Groq API key | Whisper + Llama 3 | 1,000 Whisper req/day, generous Llama 3 limits | https://console.groq.com |
| requests | HTTP client | Always free | pip install requests |
Get your Groq API key:
- Go to https://console.groq.com
- Sign up with Google/GitHub (no credit card)
- Navigate to API Keys, create one, copy it
- Export it:
export GROQ_API_KEY="gsk_your_key_here"
Install dependencies:
pip install pyaudio requests
On macOS, if PyAudio fails to build, install portaudio first:
brew install portaudio
pip install pyaudio
On Ubuntu/Debian:
sudo apt-get install portaudio19-dev python3-pyaudio
pip install pyaudio
Step 1: Capturing System Audio with PyAudio
We need raw PCM audio in a format Groq's Whisper accepts: 16kHz mono 16-bit WAV. PyAudio gives us the raw stream; we pack it into a valid WAV container in memory.
import pyaudio
import wave
import io
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
def record_audio(duration_seconds: int = 300) -> bytes:
"""Record mono 16kHz audio and return WAV bytes."""
p = pyaudio.PyAudio()
stream = p.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK
)
print(f"Recording for {duration_seconds} seconds...")
frames = []
for _ in range(0, int(RATE / CHUNK * duration_seconds)):
data = stream.read(CHUNK)
frames.append(data)
stream.stop_stream()
stream.close()
p.terminate()
# Pack into WAV in memory
wav_buffer = io.BytesIO()
with wave.open(wav_buffer, 'wb') as wf:
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
return wav_buffer.getvalue()
This captures your default microphone. If you need system audio loopback (capturing a Zoom call), that's OS-specific—see the extensions section below.
Step 2: Transcribing with Groq's Free Whisper API
Groq's Whisper endpoint is a drop-in replacement for OpenAI's audio transcription API. Same interface, faster inference, and free.
import os
import requests
GROQ_API_KEY = os.environ["GROQ_API_KEY"]
WHISPER_URL = "https://api.groq.com/openai/v1/audio/transcriptions"
def transcribe_audio(wav_bytes: bytes, filename: str = "recording.wav") -> str:
"""Send WAV bytes to Groq Whisper, return transcript text."""
headers = {"Authorization": f"Bearer {GROQ_API_KEY}"}
files = {
"file": (filename, wav_bytes, "audio/wav")
}
data = {
"model": "whisper-large-v3-turbo",
"response_format": "text",
"language": "en"
}
response = requests.post(WHISPER_URL, headers=headers, files=files, data=data)
response.raise_for_status()
return response.text.strip()
Why whisper-large-v3-turbo? It's the fastest model on Groq, optimized for low latency while maintaining accuracy. The free tier handles it without complaint.
Step 3: Extracting Structure with Llama 3
Raw transcripts are walls of text. We need Llama 3 to pull out what matters: summary, decisions, action items, and owners.
LLAMA_URL = "https://api.groq.com/openai/v1/chat/completions"
def extract_actions(transcript: str) -> str:
"""Feed transcript to Llama 3, get structured markdown back."""
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"
}
system_prompt = """You are a precise meeting analyst. Given a transcript, output a structured markdown summary with:
- **Meeting Summary**: 2-3 sentences
- **Key Decisions**: bullet list
- **Action Items**: table with columns: Task, Owner, Deadline (if mentioned)
- **Open Questions**: bullet list of unresolved items
Be concise. If an owner or deadline isn't stated, write "Unassigned" or "Not specified". Do not invent information."""
payload = {
"model": "llama-3.1-8b-instant",
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Transcript:\n\n{transcript}"}
],
"temperature": 0.2,
"max_tokens": 1024
}
response = requests.post(LLAMA_URL, headers=headers, json=payload)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
We use llama-3.1-8b-instant for speed. The 8B parameter model is more than capable of this structured extraction task, and it returns in under a second on Groq's LPUs. Temperature 0.2 keeps it factual and consistent.
Step 4: Tying It All Together
Now we wire the three stages into a single script you can run before any meeting.
import sys
import datetime
def main():
# Parse optional duration argument
duration = int(sys.argv[1]) if len(sys.argv) > 1 else 300
print("=== Meeting Notetaker ===")
print(f"Will record for {duration} seconds ({duration//60} min)")
input("Press Enter to start recording...")
# Stage 1: Record
wav_bytes = record_audio(duration)
print(f"Recorded {len(wav_bytes)} bytes of audio")
# Stage 2: Transcribe
print("Transcribing with Groq Whisper...")
transcript = transcribe_audio(wav_bytes)
print(f"Transcript ({len(transcript)} chars):\n{transcript[:200]}...")
# Stage 3: Extract
print("Extracting action items with Llama 3...")
notes = extract_actions(transcript)
# Output
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H%M")
filename = f"meeting_notes_{timestamp}.md"
with open(filename, "w") as f:
f.write(f"# Meeting Notes — {timestamp}\n\n")
f.write(notes)
f.write(f"\n\n---\n*Raw transcript length: {len(transcript)} characters*")
print(f"\n=== Notes saved to {filename} ===")
print(notes)
if __name__ == "__main__":
main()
How to Run Your Notetaker
Save the complete script as meeting_notetaker.py, then:
export GROQ_API_KEY="gsk_your_key_here"
python meeting_notetaker.py 600 # 10-minute meeting
It'll prompt you to press Enter, then record for the specified duration. After recording, it sends the audio to Groq, gets the transcript, runs it through Llama 3, and writes meeting_notes_2025-03-15_1430.md to your working directory.
Sample output:
# Meeting Notes — 2025-03-15_1430
**Meeting Summary**: The team discussed Q2 roadmap priorities and agreed to delay the analytics dashboard in favor of the API rate limiter. Engineering will spike the rate limiter design by Friday.
**Key Decisions**:
- Analytics dashboard moved to Q3
- Rate limiter becomes top priority for sprint 6
- Customer-facing SLA docs need updating before launch
**Action Items**:
| Task | Owner | Deadline |
|------|-------|----------|
| Spike rate limiter design | Sarah | Friday |
| Update SLA documentation | Mark | Next Wednesday |
| Communicate roadmap change to sales | Priya | EOD Tuesday |
**Open Questions**:
- Do we need Redis or is in-memory sufficient for the rate limiter?
- Will the SLA changes require legal review?
Common Pitfalls and Debugging Tips
"No Default Input Device" error
PyAudio can't find your microphone. Check python -c "import pyaudio; print(pyaudio.PyAudio().get_default_input_device_info())". If it fails, your OS isn't exposing an input device. On macOS, grant terminal microphone permissions in System Settings > Privacy.
Groq returns 401 Unauthorized
Your API key isn't being picked up. Verify with echo $GROQ_API_KEY. If you set it in a different terminal session, it won't carry over.
Whisper returns garbled text Check your sample rate. Groq expects the audio to match what you declare. We're sending 16kHz mono 16-bit—if your PyAudio config is different, the transcription will be nonsense.
Llama 3 hallucinates owners
Temperature is set to 0.2 for a reason. If it's still inventing names, add "Do not assign owners unless explicitly stated in the transcript." to the system prompt.
Large files time out Groq's Whisper endpoint handles files up to 25MB. A 30-minute 16kHz mono WAV is roughly 30MB. If you're hitting limits, split the recording into chunks or reduce duration.
Sensible Extensions
Once this baseline works, here's where you can take it:
System audio loopback (capture calls)
On macOS, use BlackHole (free virtual audio driver). On Windows, use VB-Cable. Route your system output to the virtual device, then point PyAudio at that input device index. Change the input_device_index parameter in p.open() to match.
Real-time streaming transcription Instead of recording the full meeting then transcribing, stream 5-second chunks to Groq Whisper and display partial transcripts live. This requires a threaded producer-consumer pattern—PyAudio fills a queue, a worker thread sends chunks to the API.
Speaker diarization Whisper doesn't label speakers. For that, you'd need a diarization model like pyannote.audio (free, open-source) running locally. Run diarization first to get speaker segments, then transcribe each segment separately and label the output.
Integration with task tools Parse the action items table and auto-create tasks in Linear, Asana, or Notion via their APIs. The structured output from Llama 3 makes this straightforward—split on the table rows and POST to your tool's endpoint.
If you're thinking about building more AI-powered productivity tools, our Build a Resume Tailoring Chrome Extension That Rewrites Your CV for Each Job guide walks through a similar pattern: capture, process with an LLM, output structured results.
FAQ
Is Groq's free tier really free? Yes. No credit card, no trial expiration. Rate limits apply (1,000 Whisper requests/day, generous chat completions), but for personal meeting notetaking you'll never hit them.
Can I use this for confidential meetings? Audio is sent to Groq's servers for processing. Review their data usage policy—free tier data may be used for service improvement. For highly sensitive meetings, consider running Whisper locally (see our n8n guide linked above for a local Whisper.cpp approach).
What if my meeting is longer than the recording duration?
Set a longer duration: python meeting_notetaker.py 3600 for an hour. Or modify the script to record until you press Ctrl+C.
Why not use OpenAI's Whisper API directly? Groq's Whisper is faster (LPU inference) and the free tier is more generous than OpenAI's $5 credit that expires. Same model, same API format, zero cost.
How do I improve transcription accuracy for technical terms? Whisper doesn't support custom vocabularies via the API. The workaround: add a post-processing step with Llama 3 that corrects known domain terms. Feed it a glossary in the system prompt.
Can this run on a Raspberry Pi? PyAudio works on ARM. The heavy lifting happens on Groq's servers, so yes—your Pi just needs to capture audio and make HTTP requests. Perfectly viable for a dedicated meeting recorder.
This guide is part of FDE Coach's series on building practical AI tools. If you want to go deeper on shipping production-grade integrations under real-world constraints, our coaching programs cover exactly this—from architecture decisions to deployment patterns that hold up when customers are watching.
Want to build like a Forward Deployed Engineer?
FDE Coach is a cohort-based program in frontend, backend, AWS, and AI. Build real products and get referred to 200+ hiring partners.
Explore the program