Scalability Analysis β Free Tier Limits
P2 - MediumMedSchools.ai Scalability Analysis
Report Date: February 11, 2026
Analyst: Sage Patel, Business Analyst
Project: MedSchools.ai
Current Infrastructure: Supabase Free Tier + Vercel Hobby (Free) Tier
Executive Summary
MedSchools.ai can comfortably handle 1,000 visitors/month on free tiers with current architecture. The application will hit Supabase database egress limits first at approximately 8,000-10,000 visitors/month, requiring either significant optimization or upgrade to Supabase Pro ($25/month). At 100,000 visitors/month, a full production infrastructure (Supabase Pro + Vercel Pro) will cost approximately $85-120/month depending on usage patterns.
Key Bottlenecks (in order of concern):
- Supabase database egress (2 GB/month free limit)
- Vercel function invocations (1M/month, but heavy AI/RAG queries consume this quickly)
- Supabase database connections (pooling required as traffic increases)
- OpenAI API costs (not infrastructure, but scales with chat usage)
Critical Optimization Needed: Aggressive caching of chat responses, database query results, and school data can extend free tier capacity by 3-5x.
1. Current Infrastructure Limits
Supabase Free Tier Limits (2026)
| Resource | Free Tier Limit | Current Usage (est.) | Headroom |
|---|---|---|---|
| Database Storage | 500 MB | ~150 MB (blog + school data + user profiles) | β High |
| Database Egress | 2 GB/month | ~200 MB/month (low traffic) | β οΈ Primary Bottleneck |
| Total Bandwidth | 10 GB/month (5 GB cached + 5 GB uncached) | ~500 MB/month | β High |
| Auth MAU | 50,000 MAU | <100 MAU | β Very High |
| File Storage | 1 GB | ~50 MB (avatars, images) | β High |
| Edge Functions | 500,000 invocations | Not currently used | β N/A |
Key Findings:
- Database egress is the critical limit. Each page load that queries the database counts toward this limit.
- Current schema includes multiple large tables:
blog_posts,medical_school,content_chunks(RAG embeddings), user data. - The chat endpoint (
/api/chat) performs 6-8 database queries per request, including heavy RAG vector similarity searches.
Vercel Hobby (Free) Tier Limits (2026)
| Resource | Free Tier Limit | Current Usage (est.) | Headroom |
|---|---|---|---|
| Fast Data Transfer | 100 GB/month | ~5 GB/month | β High |
| Edge Requests | 1M/month | ~50K/month | β High |
| Function Invocations | 1M/month | ~10K/month | β Moderate |
| Active CPU | 4 hours/month | ~0.5 hours/month | β οΈ Moderate |
| Provisioned Memory | 360 GB-hrs/month | ~40 GB-hrs/month | β High |
| Image Transformations | 5,000/month | <500/month | β High |
| ISR Reads | 1M/month | Not heavily used yet | β High |
Key Findings:
- Vercel free tier is significantly more generous than Supabase for current traffic levels.
- The blog uses good caching headers (
s-maxage=3600, stale-while-revalidate=86400), reducing function invocations. - AI chat feature consumes the most function resources (OpenAI API calls, embeddings, RAG queries).
2. Database Query Efficiency Analysis
Query Patterns Identified
Total Database Interactions:
- 86 Supabase client queries (
.from()calls) - 17 direct Postgres queries (
sqltemplate literals for RAG/performance)
Most Expensive Endpoints (by DB load):
/api/chat(AI Chat) - π΄ VERY EXPENSIVE- Performs 4 parallel queries on every request:
- User profile (11 columns)
- School list with joins (7 columns)
- Activities (top 15, 6 columns)
- Essay drafts (recent 5, 4 columns with joins)
- Plus: Vector similarity search on
content_chunks(RAG, computationally expensive) - Estimated cost: 200-400 KB database egress per chat message
- Optimization opportunity: Cache user context for 5-10 minutes
- Performs 4 parallel queries on every request:
/api/schools/[id](School Details) - π‘ MODERATE- 2 queries: school data + artifacts with categories
- Processes all artifacts client-side to count categories
- Estimated cost: 50-100 KB per request
- Optimization opportunity: Pre-compute category counts in database
/routes/blog/+page.server.ts(Blog Index) - π’ WELL OPTIMIZED- Uses
Promise.all()for parallel queries (good!) - Has cache headers (3600s cache, 86400s stale-while-revalidate)
- Estimated cost: 20-30 KB per request (cached)
- Current state: β Already optimized
- Uses
Good Patterns Found:
β
Parallel queries with Promise.all()
β
Selective column selection (not SELECT *)
β
Cache-Control headers on blog pages
β
Pagination on blog posts (12 per page)
Anti-Patterns / Issues:
β No caching on AI chat responses (every message = full DB query set)
β Category stats computed client-side instead of in DB
β No connection pooling visible (could cause issues at scale)
β Vector similarity searches on every chat message (no semantic cache)
3. Capacity Estimates & Bottleneck Analysis
Assumptions ("Reasonable Usage")
- Average session duration: 3 minutes
- Pages per visit: 5 pages
- Return visitor rate: 20%
- Page mix: 60% static (blog, directory), 40% dynamic (dashboard, chat)
- Chat usage: 30% of logged-in users send at least 1 chat message per session
- Average chat messages per session: 3 messages
Calculations
Estimated Database Egress per Visitor:
- Blog/static pages: 20 KB Γ 3 pages = 60 KB
- Dynamic pages: 50 KB Γ 2 pages = 100 KB
- Chat (if used): 300 KB Γ 3 messages = 900 KB
- Average per visitor (non-chat): ~160 KB
- Average per visitor (with chat): ~1,060 KB (1.06 MB)
Concurrent User Capacity:
| Metric | Free Tier Limit | Estimated Capacity |
|---|---|---|
| Supabase DB connections | ~15-20 (free tier, no pooler) | ~25-30 concurrent users (assuming 0.5s avg query time) |
| Vercel function concurrency | 1000 (soft limit, Hobby) | 100+ concurrent users (assuming 50ms avg response) |
Bottleneck: Database connections will be the first concurrent user limit at ~25-30 simultaneous active users. This is manageable at low traffic but becomes critical during traffic spikes.
4. Scaling Milestones & Cost Projections
Milestone 1: 1,000 Visitors/Month
Can free tier handle this? β YES, comfortably
Estimated Usage:
- Database egress:
- Non-chat users (70%): 700 Γ 160 KB = 112 MB
- Chat users (30%): 300 Γ 1,060 KB = 318 MB
- Total: ~430 MB/month (of 2 GB limit)
- Vercel bandwidth: ~2 GB (of 100 GB limit)
- Function invocations: ~50K (of 1M limit)
Infrastructure Cost: $0/month
Action Required: None
Risk Level: π’ LOW
Milestone 2: 10,000 Visitors/Month
Can free tier handle this? β οΈ NO - Database egress exceeded
Estimated Usage:
- Database egress:
- Non-chat users (70%): 7,000 Γ 160 KB = 1,120 MB
- Chat users (30%): 3,000 Γ 1,060 KB = 3,180 MB
- Total: ~4,300 MB/month (215% of 2 GB limit) β
- Vercel bandwidth: ~20 GB (of 100 GB limit) β
- Function invocations: ~500K (of 1M limit) β
- Active CPU: ~3 hours (of 4 hours limit) β οΈ
Required Upgrades:
- Supabase Pro: $25/month (includes 8 GB egress, then $0.09/GB overage)
- Vercel Hobby: Still sufficient (free)
Infrastructure Cost: $25/month
Optimization Alternative:
If aggressive caching is implemented (see Section 5), database egress could be reduced by ~60%, keeping within free tier:
- Cache chat context for 10 minutes: -50% DB egress from chat
- Cache school details for 1 hour: -30% DB egress from school pages
- Optimized total: ~1,720 MB/month (within 2 GB limit) β
Action Required: Implement caching layer OR upgrade to Supabase Pro
Risk Level: π‘ MODERATE
Milestone 3: 100,000 Visitors/Month
Can free tier handle this? β NO - Multiple limits exceeded
Estimated Usage:
- Database egress: ~43,000 MB/month (43 GB) - requires Supabase Pro + overage fees
- Vercel bandwidth: ~200 GB - exceeds Hobby limit (100 GB)
- Function invocations: ~5M - exceeds Hobby limit (1M)
- Active CPU: ~30 hours - exceeds Hobby limit (4 hours)
Required Upgrades:
Supabase Pro: $25/month base + overage
- Included: 8 GB egress
- Overage: 35 GB Γ $0.09/GB = $3.15/month
- Supabase Total: ~$28/month
Vercel Pro: $20/month per seat + usage
- Included: 1 TB bandwidth, 10M edge requests, enhanced compute
- Overage: Likely within included limits
- Vercel Total: ~$20/month (base)
With Aggressive Caching (60% reduction):
- Database egress: ~17 GB (8 GB included + 9 GB overage = $0.81)
- Optimized Supabase: ~$26/month
- Vercel Pro: ~$20/month
- Total Infrastructure: ~$46/month
Without Caching:
- Supabase: ~$28/month
- Vercel Pro: $20/month + potential overages (~$15/month)
- Total Infrastructure: ~$63/month
Additional Costs at This Scale:
- OpenAI API: ~$50-100/month (3,000 chat sessions Γ 10 messages avg Γ $0.002/request)
- Monitoring/observability: $10-20/month (optional but recommended)
Total Operating Cost (with optimization): $85-120/month
Total Operating Cost (without optimization): $150-200/month
Action Required: Upgrade to Supabase Pro + Vercel Pro, implement caching
Risk Level: π‘ MODERATE (costs manageable with optimization)
5. Optimization Opportunities
Immediate Actions (Extend Free Tier by 3-5x)
1. Implement Response Caching for AI Chat π΄ CRITICAL
Impact: Reduce DB egress by 50-60%
Implementation:
// Cache user context for 10 minutes
const CONTEXT_CACHE_TTL = 600; // 10 minutes
const userContextKey = `user:${userId}:context`;
// Check cache first
let userContext = await redis.get(userContextKey);
if (!userContext) {
// Fetch from DB (current 4 queries)
userContext = await fetchUserContext(userId);
await redis.setex(userContextKey, CONTEXT_CACHE_TTL, JSON.stringify(userContext));
}
Requirements:
- Add Redis (Upstash free tier: 10K commands/day) or use Vercel KV
- Implement cache invalidation on profile updates
Effort: Medium (2-3 days)
Cost: $0 (Upstash free tier sufficient for <10K visitors/month)
2. Pre-compute School Category Stats π‘ HIGH PRIORITY
Impact: Reduce API response time by 40%, DB load by 20%
Current Issue:
// Currently: Fetch ALL artifacts, compute categories client-side
const { data: artifacts } = await supabase
.from('school_artifacts')
.select('categories, worth_referencing')
.eq('medical_school_id', medicalSchoolId);
// Client-side processing (inefficient)
artifacts.forEach(artifact => {
if (artifact.categories) {
categories.forEach(category => {
categoryStats[category].count++;
});
}
});
Optimization:
Create a materialized view or scheduled update:
CREATE MATERIALIZED VIEW school_category_stats AS
SELECT
medical_school_id,
category,
COUNT(*) as artifact_count,
SUM(CASE WHEN worth_referencing = 'high' THEN 1 ELSE 0 END) as high_value_count
FROM school_artifacts, unnest(categories) as category
GROUP BY medical_school_id, category;
-- Refresh nightly (artifacts don't change frequently)
REFRESH MATERIALIZED VIEW CONCURRENTLY school_category_stats;
Effort: Low (1 day)
Cost: $0 (database optimization)
3. Enable Incremental Static Regeneration (ISR) for School Pages π‘ MEDIUM PRIORITY
Impact: Reduce function invocations by 70%, improve page load speed
Implementation:
// In medical-schools/[school_id]/+page.server.ts
export const config = {
isr: {
expiration: 3600, // Revalidate every hour
}
};
Benefits:
- School data rarely changes β perfect for ISR
- First visitor generates page, subsequent visitors get cached version
- Vercel handles cache invalidation automatically
Effort: Low (1-2 hours)
Cost: $0 (within Vercel free tier ISR limits)
4. Implement Database Connection Pooling π’ MEDIUM PRIORITY
Impact: Support 5-10x concurrent users without upgrade
Current Issue: Each serverless function creates new DB connection
Solution: Use Supabase Pooler (free on Pro tier) or PgBouncer
// Update DATABASE_URL to use pooler
const DATABASE_URL = process.env.SUPABASE_POOLER_URL || process.env.DATABASE_URL;
Note: Requires Supabase Pro ($25/month) for built-in pooler
Alternative: Self-host PgBouncer (free but requires maintenance)
Effort: Low (2-3 hours)
Cost: $0 (if already on Supabase Pro)
5. Add Semantic Caching for RAG Queries π’ LOWER PRIORITY
Impact: Reduce OpenAI API costs by 30-40%, reduce DB vector searches
Implementation:
Cache similar questions and their RAG context:
// Before expensive vector search
const queryEmbedding = await getCachedEmbedding(message);
const cachedResults = await findSimilarCachedQuery(queryEmbedding, threshold=0.95);
if (cachedResults) {
return cachedResults; // Skip DB vector search
}
Effort: High (5-7 days)
Cost: $0 (cache storage minimal)
Summary of Optimizations
| Optimization | Impact | Effort | Priority | Cost Savings at 10K visitors |
|---|---|---|---|---|
| Chat response caching | π΄ Critical | Medium | 1 | Stay on free tier ($25/month) |
| Pre-compute category stats | π‘ High | Low | 2 | Negligible |
| ISR for school pages | π‘ Medium | Low | 3 | Negligible |
| Connection pooling | π’ Medium | Low | 4 | Enables scale without upgrade |
| Semantic RAG caching | π’ Lower | High | 5 | ~$15/month (OpenAI costs) |
Combined Impact: With optimizations #1-3 implemented, free tier can handle ~15,000 visitors/month instead of 8,000.
6. Upgrade Path & Timeline
Recommended Upgrade Strategy
Phase 1: 0-5,000 visitors/month
ββ Infrastructure: FREE (Supabase Free + Vercel Hobby)
ββ Optimizations: Implement ISR for school pages (#3)
ββ Cost: $0/month
Phase 2: 5,000-15,000 visitors/month
ββ Infrastructure: FREE (with optimizations)
ββ Optimizations Required: Chat caching (#1), Category pre-compute (#2)
ββ Dependencies: Add Upstash Redis (free tier) or Vercel KV
ββ Cost: $0/month
Phase 3: 15,000-50,000 visitors/month
ββ Infrastructure: Supabase Pro ($25/month) + Vercel Hobby (free)
ββ Optimizations: Connection pooling (#4)
ββ Benefits: 8 GB DB egress, connection pooler, daily backups
ββ Cost: $25-30/month
Phase 4: 50,000-150,000 visitors/month
ββ Infrastructure: Supabase Pro + Vercel Pro
ββ Optimizations: All above + semantic caching (#5)
ββ Benefits: Enhanced compute, 1TB bandwidth, advanced observability
ββ Cost: $50-80/month (infrastructure) + $50-100/month (OpenAI)
Phase 5: 150,000+ visitors/month
ββ Infrastructure: Supabase Pro + Vercel Pro + CDN
ββ Optimizations: Read replicas, edge caching, dedicated instances
ββ Considerations: May need Team tier ($25/month) for Supabase
ββ Cost: $150-300/month
7. Risk Factors & Unexpected Breaking Points
High-Risk Scenarios
1. Viral Traffic Spike π΄
Scenario: Post goes viral on Reddit/SDN, 10K visitors in one day
Impact: Supabase free tier egress (2 GB) exhausted in hours
Mitigation:
- Set up billing alerts at 75% of egress limit
- Have Supabase Pro credit card ready to activate instantly
- Implement rate limiting on chat endpoint (10 messages/user/hour)
2. Bot/Scraper Attack π΄
Scenario: SEO scraper or competitor bot hits site repeatedly
Impact: Egress limit hit, potential DDOS
Mitigation:
- Implement Vercel Edge Middleware bot detection (free on Hobby)
- Add Cloudflare (free tier) in front of Vercel for DDOS protection
- Rate limit API endpoints (express-rate-limit or Upstash Rate Limit)
3. Memory Leak in Serverless Functions π‘
Scenario: Function memory usage grows, hits 1 GB limit
Impact: Functions timeout, site becomes unusable
Mitigation:
- Monitor Vercel function metrics (memory, duration)
- Set function timeout to 10s (default is 60s on Hobby)
- Implement proper connection closing in DB queries
4. OpenAI API Cost Explosion π‘
Scenario: Chat feature becomes popular, AI costs spike
Impact: $500+ monthly OpenAI bill
Mitigation:
- Set OpenAI organization spending limit ($100/month hard cap)
- Implement chat message limits (20 messages/day for free users)
- Cache chat responses for common questions
- Consider cheaper models (GPT-4o-mini for simple queries)
5. Database Storage Growth π’
Scenario: User-generated content (essays, activities) grows beyond 500 MB
Impact: Database full, new signups fail
Mitigation:
- Archive old essay drafts (>6 months) to S3/Cloudflare R2
- Implement soft delete with cleanup jobs
- Monitor storage usage monthly
8. Competitive Benchmarking
Comparable SaaS Apps on Free Tiers
| Metric | MedSchools.ai | Typical SaaS | Assessment |
|---|---|---|---|
| Max free tier users | ~5,000-8,000/mo (current) | 1,000-10,000/mo | β On par |
| Database queries per page | 2-8 queries | 1-3 queries | β οΈ Above average |
| Use of caching | Minimal (blog only) | Aggressive (Redis, CDN) | β Below standard |
| Serverless optimization | Good (parallel queries) | Excellent (edge, ISR) | π‘ Room for improvement |
Key Takeaway: With proper caching, MedSchools.ai can compete with industry-standard SaaS apps on free tiers.
9. Monitoring & Alerts
Critical Metrics to Track
Supabase (via Dashboard):
- Database egress (daily): Alert at 75% of monthly limit (1.5 GB)
- Database size: Alert at 400 MB (80% of 500 MB limit)
- Connection count: Alert if >80% pool utilization
Vercel (via Dashboard + Analytics):
- Function invocations: Alert at 750K/month (75% of 1M limit)
- Active CPU hours: Alert at 3 hours (75% of 4 hour limit)
- Bandwidth: Alert at 75 GB (75% of 100 GB limit)
- Function errors: Alert if error rate >5%
Application-Level (Custom):
- Chat messages per day: Monitor for unusual spikes
- OpenAI API costs: Daily budget check
- Average DB query time: Alert if >500ms (performance degradation)
Recommended Tools:
- Vercel Analytics: Built-in, free on Hobby tier
- Supabase Logs: Built-in, 1-day retention on free tier
- Uptime monitoring: UptimeRobot (free, 50 monitors)
- Error tracking: Sentry (free tier: 5K events/month)
10. Recommendations
Immediate Actions (Next 7 Days)
- β Set up billing alerts on Supabase (1.5 GB egress) and Vercel (750K functions)
- β Implement ISR for school detail pages (1-2 hours, high impact)
- β Add cache headers to all API routes (2-3 hours)
- β Create monitoring dashboard (Google Sheets or Notion tracking daily metrics)
Short-Term (Next 30 Days)
- β Implement chat context caching with Upstash Redis (2-3 days)
- β Pre-compute school category stats (materialized view, 1 day)
- β Add rate limiting to chat endpoint (1 day)
- β Set up OpenAI spending limit ($100/month cap)
Before Scaling to 10K Visitors
- β Complete all optimizations in Section 5 (#1-3 critical)
- β Load test chat and school endpoints (simulate 100 concurrent users)
- β Prepare upgrade path (have Supabase Pro credit card ready)
- β Document runbook for scaling emergencies
Future Enhancements (3-6 Months Out)
- Read replicas for Supabase (when traffic >50K/month)
- Edge Functions for global low-latency (Vercel Pro)
- Dedicated instances for predictable performance (Supabase Team tier)
- Multi-region deployment for international users
Appendix: Infrastructure Cost Calculator
Interactive Cost Estimator (Spreadsheet Formula)
Monthly Visitors (V) = [input]
Chat Engagement Rate (C) = 30%
Pages per Visit (P) = 5
DB Egress per Page (E) = 50 KB (optimized) or 200 KB (unoptimized)
Chat DB Egress (CE) = 300 KB per message Γ 3 messages = 900 KB
Total DB Egress = (V Γ (1-C) Γ P Γ E) + (V Γ C Γ CE)
Supabase Cost:
- If egress < 2 GB: $0
- If 2 GB < egress < 8 GB: $25/month (Pro)
- If egress > 8 GB: $25 + ((egress - 8) Γ $0.09)
Vercel Cost:
- If V < 50,000: $0 (Hobby)
- If V > 50,000: $20/month (Pro)
Total Infrastructure Cost = Supabase Cost + Vercel Cost
Example Calculations
10,000 visitors/month (optimized):
- DB Egress = (10,000 Γ 70% Γ 5 Γ 50 KB) + (10,000 Γ 30% Γ 900 KB)
- = (1.75 GB) + (2.7 GB) = 4.45 GB
- Supabase: $25/month (Pro tier, within 8 GB included)
- Vercel: $0/month (Hobby)
- Total: $25/month
10,000 visitors/month (unoptimized):
- DB Egress = (10,000 Γ 70% Γ 5 Γ 200 KB) + (10,000 Γ 30% Γ 900 KB)
- = (7 GB) + (2.7 GB) = 9.7 GB
- Supabase: $25 + (1.7 GB Γ $0.09) = $25.15/month
- Vercel: $0/month
- Total: $25.15/month
Conclusion
MedSchools.ai's current architecture is well-suited for initial growth on free tiers. With strategic optimizationsβparticularly caching of chat contexts and ISR for school pagesβthe application can scale to 15,000 visitors/month at $0/month, and 100,000 visitors/month for ~$85-120/month (including AI costs).
The primary scaling bottleneck is Supabase database egress, which is easily mitigated through caching and query optimization. Vercel's free tier is highly generous and will not be a constraint until 50,000+ visitors/month.
Next steps: Implement the immediate actions (Section 10) within 7 days to build a foundation for sustainable growth.
Report prepared by: Sage Patel, Business Analyst
Date: February 11, 2026
Questions? Contact via company task board or Slack.
Created: Wed, Mar 4, 2026, 11:01 PM by bob
Updated: Wed, Mar 4, 2026, 11:01 PM
Last accessed: Sat, Mar 28, 2026, 3:02 AM
ID: cc7958a7-b7b6-4b91-9680-f3308844e9d1