Rankioz: Designing a Multi-Tenant SEO Platform
Rankioz is a multi-tenant SaaS platform that combines AI-powered content generation with end-to-end search engine optimization. I designed and built the entire system: backend architecture, infrastructure, data model, async pipelines, billing, and integrations, taking it from an empty repository to a production deployment serving real users.
Context
The SEO tools market is fragmented. Content teams typically juggle separate tools for keyword research, content writing, rank tracking, competitor analysis, and publishing. Each tool has its own data silo, its own subscription, and its own learning curve. The idea behind Rankioz was to collapse that stack into a single platform, and layer AI on top to automate the most time-consuming parts.
The core product hypothesis: if you crawl a website, extract its brand voice, build a knowledge base from its existing content, and combine that with real-time keyword intelligence, you can generate SEO-optimized articles that actually sound like the brand wrote them.
That required a system that could crawl websites at scale, train brand voice models per domain, run retrieval-augmented generation against a vector knowledge base, orchestrate publishing to external CMS platforms, and track the search performance of everything it produces, all within a multi-tenant architecture where each workspace has its own billing, team, and data boundaries.
My Role
Technical Founder & Backend Engineer
I owned all technical decisions end-to-end:
- System architecture and data model design
- Backend implementation (Django REST Framework, PostgreSQL, Celery, Redis)
- Infrastructure setup and deployment (Railway)
- External API integration design (Google Ads, Search Console, MOZ, OpenAI, Paddle, and 10+ others)
- Security model, authentication, and authorization
- Background processing architecture and task queue design
- Billing system and subscription lifecycle management
Architecture
System Overview
The platform runs as a Django REST API backed by PostgreSQL, with Celery workers processing heavy workloads asynchronously. The frontend is a separate Next.js application that communicates over HTTPS with token-based authentication.
Multi-Tenant Data Model
Every resource in Rankioz is scoped to a Workspace (the tenant boundary). Each workspace contains websites, team members, a billing subscription, and a credit balance. Users can belong to multiple workspaces with different roles (Owner, Admin, Editor, Viewer), and switch between them within the same session.
The data model follows a strict hierarchy:
Workspace
├── Subscription (1:1)
├── Credit Balance
├── Team Members (with roles)
└── Websites
├── Crawled Pages
├── Brand Voice Model
├── Knowledge Base (embeddings)
├── Keyword Universe
├── Articles
├── Meta Tag Audits
├── Rank Tracking Data
└── CMS IntegrationsOne non-trivial decision was the shared index system. When the same domain appears across multiple workspaces, the system avoids redundant crawling and AI processing by linking dependent websites to a source website via a source_website foreign key. Crawled pages, brand profiles, and embeddings are shared. Articles, rank snapshots, and billing remain independent per workspace.
Backend Structure
The backend is organized into 19 Django apps. The most important ones:
| App | Responsibility |
|---|---|
core | Workspace, Website, Membership |
billing | Paddle subscriptions, credit system |
crawler | Website crawling and content extraction |
ai | Brand profiles, embeddings, RAG pipeline |
seo_agent | Autopilot article generation and publishing |
rank_tracking | Keyword tracking and GSC integration |
page_optimizer | AI page optimization with WordPress sync |
google_ads | Keyword research via Google Ads API |
Plus 11 supporting apps covering content management, competitor analysis, backlink opportunities, meta tag auditing, affiliates, and scraping utilities.
Business logic lives in dedicated services.py files within each app, keeping views thin and testable. This service-layer pattern was a deliberate choice to avoid the Django anti-pattern of fat views or fat models.
Key Technical Decisions
1. The Content Generation Pipeline
The most architecturally complex part of the system is the content generation pipeline. It chains multiple async tasks together, each depending on the output of the previous step:
Each step runs as a Celery task. The crawl task chains into brand profile generation, which chains into embedding generation, which chains into meta tag auditing. A single website addition triggers a multi-step async pipeline that can take minutes to complete, with real-time progress updates pushed to the frontend via polling.
The critical design choice was when to deduct credits. Credits are consumed at article generation time, not at publish time. This simplifies the billing model (generation is the expensive operation) but means failed generations still consume a credit, a known trade-off documented for future improvement.
2. RAG-Based Content Generation
Articles are not generated from a generic prompt. The system uses Retrieval-Augmented Generation (RAG) to ground every article in the website's actual content:
- During crawling, pages are chunked into segments of up to 1,500 characters each.
- Each chunk receives a vector embedding via OpenAI's
text-embedding-3-smallmodel. - When generating an article, the system performs a similarity search against these embeddings to retrieve the most relevant content chunks.
- Those chunks, combined with the brand voice profile and real-time keyword data, form the context window for GPT-4.1.
This produces articles that reflect the brand's existing terminology, style, and subject matter expertise, rather than generic AI output.
3. Background Processing Architecture
Not all async work is equal. A crawling job that processes 500 pages has very different resource and latency characteristics than an AI call that generates a meta description. To handle this, I designed a multi-queue Celery architecture:
| Queue | Purpose | Characteristics |
|---|---|---|
crawler | Scrapy + Playwright crawling, brand profile generation, embeddings | Heavy, long-running |
ai_short | Article generation, keyword enrichment, image generation | Latency-sensitive |
email | Transactional emails, trial lifecycle, rank reports | Fire-and-forget |
celery | Default queue for general and scheduled tasks | Mixed |
Celery Beat handles periodic scheduling: nightly keyword tracking (8:00 AM UTC), rank change email reports (8:10 AM UTC), scheduled article publishing (every 5 minutes), trial email lifecycle (9:00 AM UTC), and WebPageTest result polling (every 2 minutes).
For tasks that must not run concurrently, like WebPageTest processing where duplicate requests waste API budget, I implemented distributed locking via Redis SET NX EX with automatic TTL expiry.
4. Multi-Integration Orchestration
The platform integrates with 15+ external APIs. Each integration has its own authentication model, rate limits, error characteristics, and data format.
| Integration | Auth Model | Purpose |
|---|---|---|
| Google Ads API | OAuth 2.0 + Developer Token | Keyword research data |
| Google Search Console | OAuth 2.0 | Rank tracking and analytics |
| OpenAI API | API Key | Content generation, embeddings, meta suggestions |
| Google Gemini | API Key | Featured image generation |
| Paddle Billing | API Key + Webhooks | Subscriptions and payments |
| MOZ API | API Key | Domain authority metrics |
| Firecrawl API | API Key | Website crawling |
| SerpAPI | API Key | SERP results for competitor analysis |
| DataForSEO API | Basic Auth | Backlink gap analysis |
| WebPageTest API | API Key | Core Web Vitals and performance |
| WordPress REST API | Application Password | Content publishing and sync |
| Webflow API | Bearer Token | Content publishing |
| Postmark | Server Token | Transactional email |
Each integration is encapsulated in its own service layer with caching, retry logic, and error categorization. For example, the Google Ads keyword research service checks a local cache before making API calls and batches uncached keywords to minimize request count. DataForSEO has explicit daily budget controls and request-per-minute rate limiting.
5. Billing and Subscription Lifecycle
Billing is handled through Paddle as the merchant of record, with a credit-based consumption model layered on top:
- Each plan allocates a monthly credit quota (reset on billing cycle renewal).
- Generating an article costs 1 credit.
- Users can purchase additional credit packs (10, 25, or 100) as one-time transactions.
- Every credit change is recorded in a
CreditLedgerEntryaudit table with a reason code.
The subscription lifecycle is driven by Paddle webhooks: subscription.created, subscription.updated, subscription.canceled, subscription.past_due, and transaction.completed. Each webhook updates both the Subscription model and the denormalized Workspace.billing_plan field. Plan upgrades take effect immediately with prorated charges; downgrades are scheduled for the end of the billing period.
The trial system gives new users 3 days and 3 article credits with a 500-page crawl limit. Automated lifecycle emails (Welcome, Progress, Trial Ending Soon) are triggered via Celery Beat and tracked in a TrialEmailLog to ensure idempotent delivery.
6. Security Model
Security decisions across the platform:
- Authentication: Token-based for the API, Google OAuth via
allauthfor social login, and a separate impersonation token system for admin debugging. - Encryption at rest: Sensitive API tokens (Webflow, WordPress credentials) are encrypted using Fernet symmetric encryption derived from Django's
SECRET_KEY. - Custom security middleware: Blocks known malicious request patterns, including PHP file requests, WordPress admin paths, and config file probes, before they reach the application layer.
- RBAC: Four-role permission model (Owner, Admin, Editor, Viewer) enforced at the API level through custom permission classes.
- Subscription gating: Custom permission classes gate feature access based on billing status.
Infrastructure and Deployment
The platform runs on Railway with three process types:
- Web: Gunicorn serving the Django API
- Worker: Celery worker consuming all four queues
- Beat: Celery Beat scheduler for periodic tasks
PostgreSQL 15 handles all relational data. Redis serves as both the Celery message broker and the distributed lock store. Static files are served via WhiteNoise with compressed manifests.
Reflections
Building Rankioz reinforced a few principles I carry into every system I design:
Start with the data model. The multi-tenant workspace → website → resource hierarchy was the single most important design decision. Getting that right early meant every feature built on top of it, including billing, team management, content generation, and rank tracking, had a clear scoping model from day one.
Separate queues for separate workloads. Mixing crawling tasks (minutes-long, CPU-heavy) with AI generation tasks (seconds-long, latency-sensitive) on the same queue creates unpredictable latency. Dedicated queues with different worker configurations solved this cleanly.
Treat external APIs as unreliable by default. With 15+ integrations, something is always degraded. Every integration service has its own caching layer, retry policy, and error categorization. The system should never crash because an external API returns an unexpected response.
Credit systems need an audit trail. A simple counter (credits_remaining -= 1) is not enough. The CreditLedgerEntry table records every credit change with a reason, timestamp, and reference. When a billing dispute arises, and they do, you need to reconstruct exactly what happened.
Shared indexes are worth the complexity. The source_website pattern saves significant compute (no duplicate crawling, no duplicate embedding generation) at the cost of more complex query logic. For a multi-tenant platform where the same domain can appear in many workspaces, the savings compound quickly.
Rankioz is live at rankioz.com (opens in new tab). Try the app at app.rankioz.com (opens in new tab). The platform continues to evolve with features like internal linking automation, content decay detection, and SERP volatility alerts on the roadmap.