Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
Primary Endpoint:
POST /v1/ratelimit/check
Request:
{
"apiKey": "string",
"endpoint": "string",
"requestCount": 1 // optional, defaults to 1 for batch operations
}
Response (Success - 200 OK):
{
"allowed": true,
"remainingTokens": 95,
"resetTime": "2026-01-29T10:26:00Z",
"retryAfter": null
}
Response (Rate Limited - 429 Too Many Requests):
{
"allowed": false,
"remainingTokens": 0,
"resetTime": "2026-01-29T10:26:00Z",
"retryAfter": 30 // seconds until next token available
}
Batch Check Endpoint (for API Gateway to pre-validate multiple requests):
POST /v1/ratelimit/check/batch
Request:
{
"checks": [
{"apiKey": "key1", "endpoint": "/api/users"},
{"apiKey": "key2", "endpoint": "/api/posts"}
]
}
Response:
{
"results": [
{"apiKey": "key1", "allowed": true, "remainingTokens": 50},
{"apiKey": "key2", "allowed": false, "retryAfter": 15}
]
}
Health Check:
GET /v1/health
Response:
{
"status": "healthy",
"cacheConnected": true,
"latencyMs": 2
}
Get Rate Limit Configuration:
GET /v1/config/limit?apiKey={apiKey}&endpoint={endpoint}
Response:
{
"apiKey": "abc123",
"endpoint": "/api/users",
"tier": "premium",
"maxRequests": 1000,
"windowMinutes": 60,
"createdAt": "2026-01-15T10:00:00Z",
"updatedAt": "2026-01-20T14:30:00Z"
}
List All Configurations for an API Key:
GET /v1/config/limits?apiKey={apiKey}
Response:
{
"limits": [
{
"endpoint": "/api/users",
"tier": "premium",
"maxRequests": 1000,
"windowMinutes": 60
},
{
"endpoint": "/api/posts",
"tier": "basic",
"maxRequests": 100,
"windowMinutes": 60
}
]
}
Create/Upgrade Plan:
POST /v1/config/plan
Request:
{
"apiKey": "abc123",
"endpoint": "/api/users",
"tier": "premium",
"maxRequests": 1000,
"windowMinutes": 60,
"payment": {
"method": "credit_card",
"token": "tok_xyz789"
}
}
Response (Success - 201 Created):
{
"status": "success",
"message": "Plan upgraded successfully",
"effectiveAt": "2026-01-29T10:25:00Z",
"config": {
"apiKey": "abc123",
"endpoint": "/api/users",
"tier": "premium",
"maxRequests": 1000,
"windowMinutes": 60
}
}
Response (Failure - 400/402):
{
"status": "failed",
"errorCode": "PAYMENT_FAILED",
"message": "Payment processing failed"
}
Update Plan:
PUT /v1/config/plan
Request:
{
"apiKey": "abc123",
"endpoint": "/api/users",
"tier": "enterprise",
"maxRequests": 10000,
"windowMinutes": 60
}
Response:
{
"status": "success",
"message": "Plan updated successfully",
"previousLimit": 1000,
"newLimit": 10000
}
Cancel Plan:
DELETE /v1/config/plan?apiKey={apiKey}&endpoint={endpoint}
Response:
{
"status": "success",
"message": "Plan cancelled successfully",
"effectiveAt": "2026-01-29T10:25:00Z"
}
Internal Endpoint (used by configsvc to notify limitersvc):
POST /v1/internal/cache/invalidate
Request:
{
"apiKey": "abc123",
"endpoint": "/api/users",
"newLimit": {
"maxRequests": 1000,
"windowMinutes": 60
}
}
Response:
{
"status": "success",
"cacheUpdated": true,
"affectedInstances": 12
}
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
Cons: what if we receive two requests from the same API key+ endpoint as cache key at same millisecond, different machines? Use a locking mechanism, where one acquires a lock to write, but this is very slow. Redis has support for atomic operations. We move all logic into one atomic step inside Redis and it guarantees no race conditions and that no other request can interrupt this logic.
what if machines have different clocks? Servers can differ in time. Let's use Redis time as the single source of truth of time.
With billions of DAU and potentially millions of QPS, the system must minimize latency while preventing cache and network saturation.
Multi layer caching strategy:
Incoming Request → API Gateway
↓
Check Layer 1 (Local Cache)
├─ Hit + Tokens Available → [Allow Request] (0.1ms latency)
└─ Miss or No Tokens
↓
Check Layer 2 (Distributed Redis)
├─ Standard Key → Hash to Redis Instance
├─ Hot Key → Hash to Shard
│ ↓
│ Atomic Redis Operation:
│ - Get current tokens
│ - Get last refill time
│ - Calculate refilled tokens
│ - Decrement if available
│ - Update last refill time
│ ↓
│ Lease tokens to Gateway Local Cache
│ ↓
│ [Allow/Deny Request] (2-3ms latency)
└─ Redis Failure
↓
Fallback to Gateway Local Cache (best effort)
↓
[Allow with degraded limits] (0.1ms latency)