Understand why one video went viral and another didn't.
HookIQ is a production-grade SaaS that lets creators compare YouTube Shorts and Instagram Reels using AI-powered transcript intelligence, engagement analysis, LangGraph RAG chat, and viral pattern detection.
User → Next.js 16 Frontend (Vercel)
↓ POST /analyze
FastAPI Backend (Docker, GCP VM)
↓ parallel extraction
┌──────────────────┐ ┌──────────────────────┐
│ YouTube Short │ │ Instagram Reel │
│ ─────────────── │ │ ────────────────── │
│ youtube- │ │ yt-dlp metadata │
│ transcript-api │ │ instaloader │
│ yt-dlp metadata │ │ yt-dlp audio dl │
└────────┬─────────┘ │ faster-whisper tiny │
│ └──────────┬───────────┘
└──────────┬─────────────┘
↓
Clean → Chunk (500t / 100o)
↓
BGE-small-en-v1.5 Embeddings (384-dim)
↓
Qdrant (self-hosted Docker, persistent)
↓
Gemini 2.0 Flash (Intelligence Layer)
├── Hook Analysis (4 scores)
├── Structure Analysis (Hook/Story/Value/CTA)
├── Viral Patterns (6 patterns)
├── Comparison Insights
└── 5 Recommendations
↓
SQLite cache (video + analysis records)
↓
POST /chat → LangGraph Agent (SSE stream)
├── Qdrant hybrid retrieval (top 5 × 2)
├── [A-Chunk-N] / [B-Chunk-N] citations
└── MemorySaver (per session memory)
| Layer | Choice | Why |
|---|---|---|
| Frontend | Next.js 16, TypeScript, Tailwind v4, shadcn/ui | App Router, dark mode, fast Turbopack builds |
| Backend | FastAPI + Python 3.12 | Async-native, automatic OpenAPI docs |
| Orchestration | LangGraph 0.2 | Stateful agent graph with per-session memory |
| Embeddings | BAAI/bge-small-en-v1.5 (sentence-transformers) | Free, 384-dim, ~50ms/batch on CPU, no API key |
| Vector DB | Qdrant (self-hosted Docker) | Free, persistent volumes, fast cosine search, filter by metadata |
| LLM | Gemini 2.0 Flash | Free tier (1M tokens/day), fast, JSON mode |
| YT Transcript | youtube-transcript-api → yt-dlp fallback | No API key, sub-second for most videos |
| IG Transcript | yt-dlp (audio) + faster-whisper tiny | Free, runs in container, ~30s on CPU |
| DB | SQLite (aiosqlite + SQLAlchemy) | Zero-config, no separate service |
| Deploy | Docker Compose on GCP VM + Vercel | Free tier for both |
| Component | Cost @ 1,000 creators/day | Cost @ 10,000 creators/day |
|---|---|---|
| Gemini 2.0 Flash | $0.00 (free tier) | ~$2/day (if exceeds free tier) |
| BGE-small embeddings | $0.00 (CPU, in-container) | $0.00 |
| Qdrant self-hosted | $0.00 (Docker volume) | $0.00 (scale VM) |
| faster-whisper tiny | $0.00 (CPU, ~30s/video) | $0.00 (queue if needed) |
| GCP e2-micro VM | $0.00 (always-free tier) | ~$13/mo (e2-small upgrade) |
| Vercel frontend | $0.00 (free tier) | $0.00 |
| Total per analysis | $0.00 | ~$0.001 |
URL caching: Same URL = zero pipeline re-run. Cost per repeat = $0.00.
- Self-hosted = zero cost (Pinecone free tier: 1 index, 100K vectors)
- Qdrant supports rich payload filtering (analysis_id + label) natively
- No egress costs, no vendor lock-in
- At 10K creators/day, a $10/month VM handles Qdrant + backend together
- OpenAI text-embedding-3-small: $0.00002/1K tokens → ~$0.02 per analysis
- BGE-small: $0.00 (runs on CPU in 50ms)
- At 1K creators/day: saves $20/day
- Quality is comparable for retrieval tasks (MTEB leaderboard top-10)
- Gemini 2.0 Flash free tier: 1M tokens/day — covers ~500+ analyses/day at zero cost
- GPT-4o: ~$0.005/1K tokens input → $0.50+ per analysis
- At 1K creators/day: saves $500/day
- Single GCP e2-micro (1 vCPU, 1GB RAM) handles ~20 concurrent analyses
- BGE model loaded once at startup (singleton)
- Whisper model loaded once at startup (singleton)
- SQLite handles 1K writes/day without issue
- URL caching means repeat URLs (common for popular videos) cost nothing
- Upgrade to e2-standard-2 (2 vCPU, 8GB RAM) → ~$50/month
- Add Redis queue (Celery) for background pipeline jobs
- Separate Qdrant to its own container with more storage
- SQLite → PostgreSQL (async) for concurrent writes
- Frontend stays on Vercel free tier (static + SSR)
- Docker + Docker Compose
- Node.js 20+
- Python 3.12 (for local dev only)
- Gemini API key (free at aistudio.google.com)
1. Clone and configure
git clone <repo>
cd hookiq
cp .env.example .env
# Edit .env — add your GEMINI_API_KEY2. Start backend + Qdrant
docker-compose up --buildBackend: http://localhost:8000
API docs: http://localhost:8000/docs
Qdrant dashboard: http://localhost:6333/dashboard
3. Start frontend
cd frontend
cp .env.local.example .env.local
# NEXT_PUBLIC_API_URL=http://localhost:8000
npm install
npm run devFrontend: http://localhost:3000
1. Create VM
# Free tier: e2-micro in us-central1, us-west1, or us-east1
gcloud compute instances create hookiq-vm \
--machine-type=e2-micro \
--zone=us-central1-a \
--image-family=debian-12 \
--image-project=debian-cloud \
--tags=http-server,https-server2. Open port 8000
gcloud compute firewall-rules create allow-hookiq \
--allow=tcp:8000 \
--target-tags=http-server3. Install Docker on VM
sudo apt-get update && sudo apt-get install -y docker.io docker-compose-plugin
sudo usermod -aG docker $USER4. Deploy
git clone <repo> && cd hookiq
echo "GEMINI_API_KEY=your_key_here" > .env
echo "FRONTEND_URL=https://your-app.vercel.app" >> .env
docker compose up -d --build5. Deploy frontend to Vercel
cd frontend
vercel --prod
# Set NEXT_PUBLIC_API_URL=http://<VM_EXTERNAL_IP>:8000 in Vercel env vars| Variable | Default | Description |
|---|---|---|
GEMINI_API_KEY |
— | Required. Google AI Studio key |
QDRANT_COLLECTION |
video_chunks |
Qdrant collection name |
FRONTEND_URL |
http://localhost:3000 |
CORS origin for frontend |
WHISPER_MODEL |
tiny |
Whisper model size (tiny/base/small) |
WHISPER_DEVICE |
cpu |
cpu or cuda |
WHISPER_COMPUTE_TYPE |
int8 |
int8 for CPU efficiency |
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
http://localhost:8000 |
Backend API URL |
| Method | Endpoint | Description |
|---|---|---|
POST |
/analyze |
Start analysis, returns {analysis_id} immediately |
GET |
/analyze/progress/{id} |
SSE stream of pipeline progress events |
GET |
/analyze/{id} |
Full analysis result (videos + insights) |
POST |
/chat |
SSE streaming RAG chat with memory |
GET |
/history |
List of past analyses |
GET |
/health |
Health check |
| Decision | Tradeoff |
|---|---|
| SQLite over PostgreSQL | Zero-config but single-writer; swap to async PostgreSQL for >100 concurrent users |
| Whisper tiny over base | ~30s vs ~60s, slight accuracy loss for short clips — acceptable for demo |
| In-process Qdrant over cluster | No network overhead, but vertical scale only |
| BGE-small over BGE-large | 5x faster, slightly lower recall — fine for 500-token chunks |
| MemorySaver over persistent store | Memory-only; restart clears chat history — use SqliteSaver for persistence |
- Persistent LangGraph checkpointer (SqliteSaver) so chat memory survives restarts
- Celery + Redis for async job queue (scale beyond single-process)
- Whisper base/small for higher accuracy on long-form content
- TikTok support via yt-dlp
- User authentication + per-user analysis history
- Batch analysis (compare 5+ videos at once)
- Export report as PDF
- Webhook notifications when analysis completes