██████╗ ███╗ ███╗███╗ ██╗██╗ █████╗ ██████╗ ██╗
██╔═══██╗████╗ ████║████╗ ██║██║██╔══██╗██╔══██╗██║
██║ ██║██╔████╔██║██╔██╗ ██║██║███████║██████╔╝██║
██║ ██║██║╚██╔╝██║██║╚██╗██║██║██╔══██║██╔═══╝ ██║
╚██████╔╝██║ ╚═╝ ██║██║ ╚████║██║██║ ██║██║ ██║
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
OmniAPI is a production-grade backend platform that lets users orchestrate calls to multiple external APIs through a single, intelligent interface. Instead of managing multiple API keys and hitting rate limits, users get one API key from OmniAPI and can intelligently call external services (weather, news, stocks, LLMs) with automatic batching, caching, rate limiting, and async queuing.
Think of it as: A smart traffic controller for API requests — making them faster, cheaper, and more reliable.
- 🔑 5+ API keys scattered everywhere
⚠️ Rate limits kill your workflow- 💰 No caching = wasted budget
- 🐢 Sequential calls = slow responses
- 👁️ Zero visibility into API usage
- 🛠️ Complex error handling for each API
- 🔒 Blocking requests tank performance
Example Scenario: A SaaS product needs weather + news + stock data. Without OmniAPI:
- 3 different API keys to manage
- Hit rate limits independently
- Same data fetched 10x per hour = wasteful
- Calls happen sequentially = slow responses
graph TB
%% Client layer
subgraph Client_Layer
WEB["🌐 Web Dashboard"]
API_CLIENT["📱 User Applications"]
end
%% OmniAPI platform
subgraph OmniAPI_Platform
AUTH["🔐 Auth Layer\nJWT + OAuth2"]
RATE["⏱️ Rate Limiter\nRedis"]
CACHE["💾 Cache Layer\nRedis TTL"]
QUEUE["📦 Job Queue\nCelery"]
ORCHESTRATOR["🎯 API Orchestrator\nFastAPI"]
WEBHOOK["🔔 Webhook Manager"]
ANALYTICS["📊 Analytics Engine"]
end
%% Data layer
subgraph Data_Layer
DB["🗄️ PostgreSQL\nMulti-Tenant DB"]
REDIS["⚡ Redis\nCache & Pub/Sub"]
end
%% External APIs
subgraph External_APIs
WEATHER["🌤️ OpenWeatherMap"]
NEWS["📰 NewsAPI"]
STOCK["📈 StockAPI"]
end
%% Deployment
subgraph Deployment
DOCKER["🐳 Docker Compose"]
CI["🔄 GitHub Actions\nCI/CD"]
RAILWAY["☁️ Railway/Render"]
end
WEB --> AUTH
API_CLIENT --> AUTH
AUTH --> ORCHESTRATOR
ORCHESTRATOR --> RATE
ORCHESTRATOR --> CACHE
ORCHESTRATOR --> QUEUE
ORCHESTRATOR --> WEBHOOK
ORCHESTRATOR --> ANALYTICS
RATE --> DB
CACHE --> REDIS
QUEUE --> REDIS
ORCHESTRATOR --> WEATHER
ORCHESTRATOR --> NEWS
ORCHESTRATOR --> STOCK
DB --> DOCKER
REDIS --> DOCKER
DOCKER --> CI
CI --> RAILWAY
style AUTH fill:#ff9999
style RATE fill:#99ccff
style CACHE fill:#99ff99
style QUEUE fill:#ffcc99
style ORCHESTRATOR fill:#ff99ff
sequenceDiagram
actor User
participant Dashboard as OmniAPI Dashboard
participant Platform as OmniAPI Platform
participant Cache as Cache Layer
participant Queue as Job Queue
participant ExtAPI as External APIs
User->>Dashboard: 1. Sign up & Create API Key
User->>Dashboard: 2. Add external API keys<br/>(Weather, News, Stock)
User->>Platform: 3. Call OmniAPI using key<br/>GET /orchestrate?services=weather,news
Platform->>Cache: Check if data cached
alt Cache Hit
Cache-->>Platform: Return cached data (instant)
else Cache Miss
Platform->>Queue: Queue external API calls
Queue->>ExtAPI: Fetch from external APIs<br/>(async, parallel)
ExtAPI-->>Queue: Return data
Queue->>Cache: Store result with TTL
Queue-->>Platform: Return data
end
Platform-->>User: Fast response with batched data
User->>Dashboard: 4. View usage analytics<br/>(API calls, cached hits, cost saved)
End-to-end guide — from signing up to making real orchestrated calls from your own application.
Go to omniapi3021.vercel.app and register.
POST /api/v1/auth/register
Content-Type: application/json
{
"full_name": "Jane Smith",
"email": "jane@example.com",
"password": "yourpassword"
}Then log in to get your JWT token pair:
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "jane@example.com",
"password": "yourpassword"
}{
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}Use access_token as a Bearer token for all dashboard API calls. The dashboard refreshes it automatically via POST /api/v1/auth/refresh when it expires.
OmniAPI calls third-party services using your own API keys for those providers. You register them once under External Services — they are AES-encrypted at rest and never returned in any response.
In the dashboard go to External Services → Add Service, or call:
POST /api/v1/external-services
Authorization: Bearer <access_token>
Content-Type: application/json
{
"service_name": "weather",
"api_key": "your_openweathermap_key",
"max_calls_per_hour": 100
}Repeat for each service. Supported service_name values:
| Value | Provider | Get a key at |
|---|---|---|
weather |
OpenWeatherMap | openweathermap.org/api |
news |
NewsAPI | newsapi.org |
stock |
StockAPI | your stock data provider |
List configured services:
GET /api/v1/external-services
Authorization: Bearer <access_token>[
{
"service_id": 1,
"service_name": "weather",
"max_calls_per_hour": 100,
"status": "active",
"created_at": "2026-06-03T10:00:00Z"
}
]A service must be in active status before it can be orchestrated. You can pause it with PATCH /api/v1/external-services/{id} by setting "status": "inactive", or remove it entirely with DELETE /api/v1/external-services/{id}.
Generate the key your application uses to authenticate with OmniAPI. In the dashboard go to API Keys → Create Key, or call:
POST /api/v1/api-keys
Authorization: Bearer <access_token>
Content-Type: application/json
{
"name": "Production App",
"expires_at": "2027-01-01T00:00:00Z"
}{
"key_id": 3,
"name": "Production App",
"raw_key": "omni_xK9mP2qL...",
"is_active": true,
"expires_at": "2027-01-01T00:00:00Z",
"created_at": "2026-06-03T10:05:00Z"
}
raw_keyis shown once only. Copy it immediately and store it in your app's environment variables. It cannot be retrieved again.
Your application passes this key in the X-API-Key header on every orchestrate call.
Send a single POST with the list of services you want and any per-service params. OmniAPI fans the calls out in parallel via Celery, caches results in Redis, and returns everything together.
POST /api/v1/orchestrate
X-API-Key: omni_xK9mP2qL...
Content-Type: application/json
{
"services": ["weather", "news"],
"params": {
"weather": { "city": "London" },
"news": { "q": "technology", "pageSize": 5 }
}
}{
"request_id": "a1b2c3d4-...",
"total_time_ms": 312,
"timestamp": "2026-06-03T10:10:00Z",
"results": [
{
"service": "weather",
"success": true,
"cache_hit": false,
"response_time_ms": 287,
"data": { "city": "London", "temp_c": 18.4, "condition": "Partly cloudy" }
},
{
"service": "news",
"success": true,
"cache_hit": true,
"response_time_ms": 0,
"data": { "articles": [ "..." ] }
}
]
}What to notice:
cache_hit: trueonnews→ served from Redis instantly, no external call madecache_hit: falseonweather→ fresh Celery task was dispatched and awaitedX-RateLimit-Remainingresponse header tells you calls remaining in the current window- A failed service returns
"success": falsewith an"error"string; other services in the same request are unaffected - Results are always ordered to match your
servicesarray
Single-service call — same shape, one item in services:
{ "services": ["stock"], "params": { "stock": { "symbol": "AAPL" } } }You can also authenticate with a Bearer JWT instead of X-API-Key when calling from a trusted server context.
Every orchestrated call is logged automatically. The Analytics page in the dashboard shows calls, cache hit rate, response times, and per-service breakdowns.
GET /api/v1/analytics/summary
Authorization: Bearer <access_token>{
"calls_today": 1420,
"calls_change": 12.5,
"cache_hit_percent": 67.3,
"avg_response_ms": 184.0,
"response_change_ms": -22.0
}GET /api/v1/analytics/usage?period=last_7d
Authorization: Bearer <access_token>Valid period values: last_24h · last_7d · last_30d
Download a CSV report of the last 30 days:
GET /api/v1/analytics/reports
Authorization: Bearer <access_token>Subscribe to real-time events so your system reacts to orchestration outcomes without polling.
POST /api/v1/webhooks
Authorization: Bearer <access_token>
Content-Type: application/json
{
"url": "https://yourapp.com/hooks/omniapi",
"event_type": "orchestrate.complete"
}{
"webhook_id": 7,
"url": "https://yourapp.com/hooks/omniapi",
"event_type": "orchestrate.complete",
"is_active": true,
"retry_count": 0,
"secret": "a3f9bc...",
"created_at": "2026-06-03T10:15:00Z"
}
secretis shown once only. Use it to verify theX-OmniAPI-Signatureheader on incoming payloads.
Supported event types:
| Event | Fires when |
|---|---|
orchestrate.complete |
All services in a request succeeded |
orchestrate.failed |
One or more services failed |
api_key.created |
A new OmniAPI key was generated |
Payload your endpoint receives:
{
"event_id": "uuid",
"event_type": "orchestrate.complete",
"tenant_id": "42",
"timestamp": "2026-06-03T10:10:05Z",
"data": {
"request_id": "a1b2c3d4-...",
"services": ["weather", "news"],
"total_time_ms": 312,
"results_summary": [
{ "service": "weather", "success": true, "cache_hit": false },
{ "service": "news", "success": true, "cache_hit": true }
]
}
}OmniAPI retries failed deliveries automatically. Check delivery history:
GET /api/v1/webhooks/{webhook_id}
Authorization: Bearer <access_token>Send a test ping to verify your endpoint is reachable:
POST /api/v1/webhooks/{webhook_id}/test
Authorization: Bearer <access_token>List all keys:
GET /api/v1/api-keys
Authorization: Bearer <access_token>Update a key's name or expiry:
PATCH /api/v1/api-keys/{key_id}
Authorization: Bearer <access_token>
Content-Type: application/json
{
"name": "Production App v2",
"expires_at": "2028-01-01T00:00:00Z"
}Permanently delete a key (any app using it gets 401 immediately):
DELETE /api/v1/api-keys/{key_id}
Authorization: Bearer <access_token>For zero-downtime rotation: create the new key → update your app env var → delete the old key.
flowchart TD
subgraph Setup["🚀 Setup"]
A([Sign Up / Log In]) --> B[Configure Services<br/>weather · news · stock]
B --> C[Get API Key<br/>omni_xxx...]
end
subgraph Request["⚡ Request Flow"]
D[Your App Sends Request<br/>X-API-Key: omni_xxx] --> E{Cache Hit?}
E -->|✅ Yes| G["⚡ Return Instantly<br/>cache_hit: true"]
E -->|❌ No| H[Dispatch Parallel Tasks<br/>Celery]
H --> I["🔄 Call External APIs<br/>in Parallel"]
I --> J["💾 Cache Results<br/>Redis TTL"]
J --> K[Return Response]
end
subgraph Notify["📊 Post-Processing"]
G --> L[Log Usage]
K --> L
L --> M["🔔 Fire Webhook<br/>orchestrate.complete"]
L --> N[Update Analytics]
end
M --> O([📈 Dashboard])
N --> O
style Setup fill:#6366f1,stroke:#000,color:#fff
style Request fill:#0070f3,stroke:#000,color:#fff
style Notify fill:#005eff,stroke:#000,color:#fff
Problem: How to safely serve multiple users without data leakage?
┌─────────────────┬──────────────────┐
│ DECISION │ Multi-Tenant DB │
├─────────────────┼──────────────────┤
│ TRADEOFF │ More complex │
│ │ queries with │
│ │ tenant filtering │
├─────────────────┼──────────────────┤
│ RESULT │ ✅ Scalable │
│ │ ✅ Cost-efficient│
│ │ ✅ Data isolated │
└─────────────────┴──────────────────┘
Implementation:
- Every table has
tenant_idforeign key - Row-level security enforced in queries
- Tenants never see each other's data
Problem: How to handle 1000s of requests/second without melting the database?
┌──────────────────┬──────────────────────┐
│ DECISION │ Redis (In-Memory) │
├──────────────────┼──────────────────────┤
│ TRADEOFF │ Data lost on crash │
│ │ (but OK for cache) │
├──────────────────┼──────────────────────┤
│ RESULT │ ✅ Sub-millisecond │
│ │ ✅ Handles millions │
│ │ ✅ Perfect for TTL │
└──────────────────┴──────────────────────┘
What Redis Does:
- Rate Limiting: "User A: 95/100 calls remaining"
- Caching: "Weather data expires in 1 hour"
- Pub/Sub: Real-time webhook events
Problem: Calling slow external APIs blocks responses for 5-10 seconds.
┌──────────────────┬──────────────────────┐
│ DECISION │ Celery Task Queue │
├──────────────────┼──────────────────────┤
│ TRADEOFF │ More infrastructure │
│ │ (Celery + Redis) │
├──────────────────┼──────────────────────┤
│ RESULT │ ✅ Instant responses │
│ │ ✅ No blocking calls │
│ │ ✅ Retries + recovery
└──────────────────┴──────────────────────┘
How It Works:
User Request → Queue Job → Return Immediately → Process in Background
Problem: Need to store users, API keys, external services, usage logs — all interconnected.
┌──────────────────┬──────────────────────┐
│ DECISION │ PostgreSQL (SQL DB) │
├──────────────────┼──────────────────────┤
│ TRADEOFF │ Slower than NoSQL │
│ │ for simple reads │
├──────────────────┼──────────────────────┤
│ RESULT │ ✅ ACID guarantees │
│ │ ✅ Complex queries │
│ │ ✅ Data integrity │
└──────────────────┴──────────────────────┘
erDiagram
USER {
int user_id PK
string email
string password_hash
string tier
timestamp created_at
}
API_KEY {
int key_id PK
int user_id FK
string key_hash
string name
timestamp expires_at
boolean is_active
}
EXTERNAL_SERVICE {
int service_id PK
int user_id FK
string service_name
string api_key_encrypted
int max_calls_per_hour
string status
}
USAGE_LOG {
int log_id PK
int user_id FK
int service_id FK
int response_time_ms
boolean cache_hit
timestamp created_at
}
WEBHOOK {
int webhook_id PK
int user_id FK
string event_type
string url
int retry_count
}
USER ||--o{ API_KEY : has
USER ||--o{ EXTERNAL_SERVICE : owns
USER ||--o{ USAGE_LOG : generates
USER ||--o{ WEBHOOK : subscribes
EXTERNAL_SERVICE ||--o{ USAGE_LOG : logs
| Component | Technology | Why This Choice |
|---|---|---|
| Backend Framework | FastAPI | Async-first, auto-docs, blazing fast |
| Database | PostgreSQL | ACID, relational data, proven at scale |
| Cache/Queue Broker | Redis | Sub-millisecond speed, pub/sub support |
| Job Queue | Celery | Distributed tasks, retries, scheduling |
| ORM | SQLAlchemy | Type hints, migration support, flexibility |
| Auth | JWT + OAuth2 | Stateless, industry standard |
| Testing | pytest | Fast, fixtures, 80%+ coverage goal |
| Containerization | Docker Compose | Dev/prod parity, easy scaling |
| CI/CD | GitHub Actions | Free, integrated with repos |
| Deployment | Railway/Render | Free tier, auto-scaling, painless |
✅ Python 3.11+
✅ Docker & Docker Compose
✅ PostgreSQL 15+
✅ Redis 7+