This enhancement plan details the implementation of SeRanking API integration to enrich IndexNow Studio's rank tracking capabilities with comprehensive keyword intelligence including search volume, intent, competition metrics, and historical trend data.
indb_keyword_keywords: Main keyword tracking table (user_id, domain_id, keyword, device_type, country_id, tags, is_active)indb_keyword_rank_history: Historical position data with datesindb_keyword_rankings: Latest position snapshotsindb_keyword_domains: Domain management and verificationindb_keyword_countries: Country/locale tracking supportindb_keyword_usage: Usage quota tracking with database triggers
indb_site_integration: Service integration management with API keys and quotas
- Keyword Intelligence: Add search volume, competition, difficulty, and intent data to keyword tracking
- API Efficiency: Implement keyword bank system to minimize external API calls
- Cost Optimization: Cache SeRanking data locally to reduce API quota usage
- Data Enrichment: Automatically enhance new keywords with market intelligence
SQL Query to Execute in Supabase:
-- Add SeRanking integration to existing indb_site_integration table
INSERT INTO "public"."indb_site_integration" (
"service_name",
"apikey",
"api_url",
"api_quota_limit",
"api_quota_used",
"quota_reset_date",
"is_active"
) VALUES (
'seranking_keyword_export',
'952945a4-5d7a-4719-16cd-5e4b8b3892d6',
'https://api.seranking.com',
10000,
0,
CURRENT_DATE + INTERVAL '1 month',
true
);SQL Query to Execute in Supabase:
-- Create keyword bank table for cached SeRanking data
CREATE TABLE "public"."indb_keyword_bank" (
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
"keyword" text NOT NULL,
"country_id" uuid NOT NULL REFERENCES "public"."indb_keyword_countries"("id"),
"language_code" text NOT NULL DEFAULT 'en',
"is_data_found" boolean NOT NULL DEFAULT false,
"volume" integer NULL,
"cpc" numeric(10,2) NULL,
"competition" numeric(3,2) NULL,
"difficulty" integer NULL,
"history_trend" jsonb NULL,
"keyword_intent" text NULL,
"data_updated_at" timestamp with time zone NOT NULL DEFAULT now(),
"created_at" timestamp with time zone NOT NULL DEFAULT now(),
"updated_at" timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT "indb_keyword_bank_pkey" PRIMARY KEY ("id"),
CONSTRAINT "indb_keyword_bank_keyword_country_unique" UNIQUE ("keyword", "country_id", "language_code")
) TABLESPACE pg_default;
-- Add RLS policies for keyword bank
ALTER TABLE "public"."indb_keyword_bank" ENABLE ROW LEVEL SECURITY;
-- Policy: Public read access for keyword data (cached data can be shared)
CREATE POLICY "keyword_bank_read_access" ON "public"."indb_keyword_bank"
FOR SELECT USING (true);
-- Policy: System-level insert/update for API responses
CREATE POLICY "keyword_bank_system_write" ON "public"."indb_keyword_bank"
FOR ALL USING (true);
-- Add indexes for performance
CREATE INDEX "idx_indb_keyword_bank_keyword" ON "public"."indb_keyword_bank" ("keyword");
CREATE INDEX "idx_indb_keyword_bank_country" ON "public"."indb_keyword_bank" ("country_id");
CREATE INDEX "idx_indb_keyword_bank_updated" ON "public"."indb_keyword_bank" ("data_updated_at");SQL Query to Execute in Supabase:
-- Add keyword intelligence columns and bank reference to existing keyword table
ALTER TABLE "public"."indb_keyword_keywords"
ADD COLUMN "keyword_bank_id" uuid NULL,
ADD COLUMN "search_volume" integer NULL,
ADD COLUMN "cpc" numeric(10,2) NULL,
ADD COLUMN "competition" numeric(3,2) NULL,
ADD COLUMN "difficulty" integer NULL,
ADD COLUMN "keyword_intent" text NULL,
ADD COLUMN "history_trend" jsonb NULL,
ADD COLUMN "intelligence_updated_at" timestamp with time zone NULL,
ADD CONSTRAINT "fk_keyword_bank"
FOREIGN KEY ("keyword_bank_id")
REFERENCES "public"."indb_keyword_bank"("id")
ON DELETE SET NULL;
-- Add indexes for keyword bank relationships and intelligence data
CREATE INDEX "idx_indb_keyword_keywords_bank_id"
ON "public"."indb_keyword_keywords" ("keyword_bank_id");
CREATE INDEX "idx_indb_keyword_keywords_search_volume"
ON "public"."indb_keyword_keywords" ("search_volume");
CREATE INDEX "idx_indb_keyword_keywords_difficulty"
ON "public"."indb_keyword_keywords" ("difficulty");Complete File Structure:
lib/integrations/seranking/
├── client/
│ ├── SeRankingApiClient.ts # HTTP client with authentication
│ ├── ApiRequestBuilder.ts # Request formatting and validation
│ └── RateLimiter.ts # API rate limiting and throttling
├── services/
│ ├── SeRankingService.ts # Core business logic orchestration
│ ├── KeywordBankService.ts # Keyword bank CRUD operations
│ ├── KeywordEnrichmentService.ts # Keyword intelligence workflow
│ ├── IntegrationService.ts # Integration settings management
│ └── BulkProcessingService.ts # Batch processing and queuing
├── validation/
│ ├── ApiResponseValidator.ts # SeRanking response validation
│ ├── KeywordValidator.ts # Keyword input validation
│ └── QuotaValidator.ts # Usage quota validation
├── errors/
│ ├── SeRankingErrorHandler.ts # Specialized error handling
│ ├── ApiErrorTypes.ts # Error type definitions
│ └── ErrorRecoveryService.ts # Retry and recovery logic
├── monitoring/
│ ├── ApiMetricsCollector.ts # Usage metrics and performance
│ ├── QuotaMonitor.ts # Quota usage monitoring
│ └── HealthChecker.ts # Integration health checks
├── queue/
│ ├── EnrichmentQueue.ts # Background job queue management
│ ├── JobProcessor.ts # Individual job processing
│ └── QueueMonitor.ts # Queue health and monitoring
├── types/
│ ├── SeRankingTypes.ts # API request/response types
│ ├── KeywordBankTypes.ts # Database entity types
│ └── ServiceTypes.ts # Service interface types
└── index.ts # Barrel exports with facade pattern
Client Layer (client/):
- SeRankingApiClient: HTTP requests, authentication, connection management
- ApiRequestBuilder: Request formatting, parameter validation, URL construction
- RateLimiter: Request throttling, quota enforcement, retry delays
Business Logic Layer (services/):
- SeRankingService: Orchestrates API calls, manages integration state
- KeywordBankService: Database operations for keyword bank table
- KeywordEnrichmentService: End-to-end keyword intelligence workflow
- IntegrationService: Manages
indb_site_integrationsettings and credentials - BulkProcessingService: Handles batch operations and queue management
Validation Layer (validation/):
- ApiResponseValidator: Validates SeRanking API response structure and data
- KeywordValidator: Validates keyword inputs and country codes
- QuotaValidator: Checks usage limits and prevents quota exhaustion
Error Handling Layer (errors/):
- SeRankingErrorHandler: Specialized handling for SeRanking API errors
- ApiErrorTypes: Comprehensive error type definitions and codes
- ErrorRecoveryService: Retry logic, circuit breaker, fallback strategies
Monitoring Layer (monitoring/):
- ApiMetricsCollector: Tracks API usage, response times, success rates
- QuotaMonitor: Real-time quota usage tracking and alerting
- HealthChecker: Integration health status and diagnostics
Queue Layer (queue/):
- EnrichmentQueue: Manages background keyword enrichment jobs
- JobProcessor: Processes individual enrichment tasks
- QueueMonitor: Queue health, job status, and performance metrics
Multi-Service Orchestration:
class KeywordEnrichmentService {
constructor(
private keywordBankService: KeywordBankService,
private seRankingService: SeRankingService,
private quotaValidator: QuotaValidator,
private keywordValidator: KeywordValidator,
private errorHandler: SeRankingErrorHandler,
private metricsCollector: ApiMetricsCollector
) {}
async enrichKeyword(keyword: string, countryCode: string): Promise<KeywordEnrichmentResult> {
const startTime = Date.now();
try {
// 1. Input validation
await this.keywordValidator.validateKeywordInput(keyword, countryCode);
// 2. Quota check
await this.quotaValidator.checkQuotaAvailability();
// 3. Check keyword bank cache first
const cachedData = await this.keywordBankService.getKeywordData(keyword, countryCode);
if (cachedData && this.isFreshData(cachedData.data_updated_at)) {
this.metricsCollector.recordCacheHit(Date.now() - startTime);
return { data: cachedData, source: 'cache' };
}
// 4. Fetch from SeRanking API with error handling
const freshData = await this.seRankingService.fetchKeywordData(keyword, countryCode);
// 5. Validate API response
const validatedData = await this.responseValidator.validateApiResponse(freshData);
// 6. Update keyword bank with fresh data
await this.keywordBankService.upsertKeywordData(validatedData);
// 7. Update quota usage
await this.quotaValidator.recordApiUsage();
this.metricsCollector.recordApiCall(Date.now() - startTime, 'success');
return { data: validatedData, source: 'api' };
} catch (error) {
await this.errorHandler.handleEnrichmentError(error, keyword, countryCode);
this.metricsCollector.recordApiCall(Date.now() - startTime, 'error');
throw error;
}
}
async bulkEnrichKeywords(keywords: KeywordRequest[]): Promise<BulkEnrichmentResult> {
// Queue-based bulk processing
const jobId = await this.bulkProcessingService.queueBulkEnrichment(keywords);
return { jobId, status: 'queued', estimatedCompletion: this.calculateEstimatedTime(keywords.length) };
}
private isFreshData(lastUpdate: Date): boolean {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
return lastUpdate > thirtyDaysAgo;
}
}New API Route Structure:
app/api/v1/integrations/seranking/
├── keyword-data/
│ ├── route.ts # GET: Single keyword intelligence
│ └── bulk/
│ └── route.ts # POST: Bulk keyword enrichment
├── quota/
│ ├── status/route.ts # GET: Current quota usage
│ ├── history/route.ts # GET: Quota usage history
│ └── alerts/route.ts # GET/POST: Quota alert settings
├── health/
│ ├── route.ts # GET: Integration health check
│ └── metrics/route.ts # GET: Performance metrics
├── admin/
│ ├── integration/route.ts # GET/PUT: Integration settings
│ ├── cache/
│ │ ├── stats/route.ts # GET: Cache statistics
│ │ └── clear/route.ts # DELETE: Clear cache
│ └── jobs/
│ ├── route.ts # GET: Job queue status
│ └── [jobId]/route.ts # GET: Individual job status
└── webhooks/
└── quota-alerts/route.ts # POST: Quota webhook endpoint
Detailed API Specifications:
Single Keyword Intelligence (GET /api/v1/integrations/seranking/keyword-data)
// Query parameters
interface KeywordDataRequest {
keyword: string;
country_code?: string;
language_code?: string;
force_refresh?: boolean;
}
// Response
interface KeywordDataResponse {
success: boolean;
data: {
keyword: string;
country_code: string;
is_data_found: boolean;
volume: number | null;
cpc: number | null;
competition: number | null;
difficulty: number | null;
keyword_intent: string | null;
history_trend: Record<string, number> | null;
source: 'cache' | 'api';
last_updated: string;
};
quota_remaining: number;
cache_hit: boolean;
}Bulk Enrichment (POST /api/v1/integrations/seranking/keyword-data/bulk)
// Request body
interface BulkEnrichmentRequest {
keywords: Array<{
keyword: string;
country_code?: string;
language_code?: string;
}>;
priority?: 'high' | 'normal' | 'low';
callback_url?: string;
}
// Response
interface BulkEnrichmentResponse {
success: boolean;
job_id: string;
status: 'queued' | 'processing' | 'completed' | 'failed';
total_keywords: number;
estimated_completion: string;
quota_required: number;
quota_available: number;
}Quota Status (GET /api/v1/integrations/seranking/quota/status)
interface QuotaStatusResponse {
success: boolean;
data: {
current_usage: number;
quota_limit: number;
quota_remaining: number;
usage_percentage: number;
reset_date: string;
daily_usage: Array<{
date: string;
usage: number;
}>;
alerts: {
enabled: boolean;
thresholds: number[];
};
};
}SQL Query to Execute in Supabase:
-- Function to automatically link keywords with bank data and populate intelligence fields
CREATE OR REPLACE FUNCTION link_keyword_to_bank()
RETURNS TRIGGER AS $$
DECLARE
bank_record RECORD;
country_code text;
BEGIN
-- Get country code from country_id
SELECT iso2_code INTO country_code
FROM indb_keyword_countries
WHERE id = NEW.country_id;
-- Find matching keyword bank record with all intelligence data
SELECT id, volume, cpc, competition, difficulty, keyword_intent, history_trend, data_updated_at
INTO bank_record
FROM indb_keyword_bank
WHERE keyword = NEW.keyword
AND country_code = COALESCE(country_code, 'us');
-- Update keyword with bank reference and intelligence data if found
IF bank_record.id IS NOT NULL THEN
NEW.keyword_bank_id = bank_record.id;
NEW.search_volume = bank_record.volume;
NEW.cpc = bank_record.cpc;
NEW.competition = bank_record.competition;
NEW.difficulty = bank_record.difficulty;
NEW.keyword_intent = bank_record.keyword_intent;
NEW.history_trend = bank_record.history_trend;
NEW.intelligence_updated_at = bank_record.data_updated_at;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create trigger for automatic keyword-bank linking
CREATE TRIGGER trigger_link_keyword_to_bank
BEFORE INSERT OR UPDATE ON indb_keyword_keywords
FOR EACH ROW
EXECUTE FUNCTION link_keyword_to_bank();SQL Query to Execute in Supabase:
-- Function to update keyword records when bank data changes
CREATE OR REPLACE FUNCTION update_keywords_on_bank_change()
RETURNS TRIGGER AS $$
BEGIN
-- Update the updated_at timestamp for related keywords
UPDATE indb_keyword_keywords
SET updated_at = now()
WHERE keyword_bank_id = NEW.id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create trigger for keyword updates when bank data changes
CREATE TRIGGER trigger_update_keywords_on_bank_change
AFTER UPDATE ON indb_keyword_bank
FOR EACH ROW
EXECUTE FUNCTION update_keywords_on_bank_change();Components to Modify:
- Keyword Addition Form: Show search volume and difficulty during keyword entry
- Keyword List View: Display enriched data (volume, CPC, competition) in table columns
- Keyword Details Modal: Full intelligence panel with historical trends
- Bulk Import: Automatic enrichment during CSV/bulk keyword imports
New Components:
components/keyword-intelligence/
├── KeywordMetricsCard.tsx # Volume, CPC, competition display
├── DifficultyIndicator.tsx # Visual difficulty scoring
├── IntentBadge.tsx # Keyword intent categorization
├── TrendChart.tsx # Historical trend visualization
└── CompetitionMeter.tsx # Competition level indicator
Admin Interface Enhancements:
- SeRanking API quota monitoring dashboard
- Usage alerts and notifications
- Manual keyword enrichment controls
- Bulk enrichment queue management
Background Job Implementation:
- Inventory Existing Keywords: Catalog all active keywords across all users
- Prioritize by Usage: Enrich most-used keywords first
- Batch Processing: Process in small batches to respect API limits
- Progress Tracking: Maintain migration progress and error logs
- User Notification: Inform users when their keywords are enriched
Automated Enrichment Logic:
- New Keywords: Immediate enrichment on creation
- Existing Data: Refresh every 30 days
- High-Value Keywords: Weekly updates for top-performing keywords
- Bulk Operations: Queue-based processing for large imports
- SeRanking API Response Validation: Test all response formats and edge cases
- Rate Limiting Compliance: Verify API call limits and throttling
- Error Handling: Test API failures, timeouts, and invalid responses
- Quota Management: Test quota tracking and reset functionality
- Keyword Bank Query Performance: Test lookup speed with large datasets
- Trigger Performance: Verify trigger efficiency with bulk operations
- Index Effectiveness: Validate query performance with proper indexing
- Data Integrity: Test foreign key relationships and constraints
- Keyword Addition Flow: Test enrichment during keyword creation
- Intelligence Display: Verify accurate data presentation
- Loading States: Test UI responsiveness during API calls
- Error States: Test graceful handling of API failures
- Execute database schema changes (indb_keyword_bank table)
- Add SeRanking integration record
- Create basic SeRanking API client
- Implement KeywordBankService foundation
- Complete SeRanking API integration
- Implement keyword enrichment flow
- Create database triggers for automatic linking
- Build API endpoints for keyword intelligence
- Enhance keyword management interface
- Create intelligence display components
- Implement quota monitoring dashboard
- Add enrichment progress indicators
- Implement existing keyword migration
- Execute comprehensive testing suite
- Performance optimization and tuning
- Documentation and deployment preparation
- Fallback Strategy: Graceful degradation when SeRanking API unavailable
- Quota Management: Strict limits to prevent quota exhaustion
- Caching Strategy: 30-day cache reduces API dependency
- Alternative Sources: Architecture allows adding multiple keyword intelligence providers
- Validation Layer: Comprehensive data validation for API responses
- Monitoring: Alert system for unusual data patterns
- Manual Override: Admin ability to correct or supplement data
- Audit Trail: Track data source and update history
- Bulk Processing: Queue-based system prevents UI blocking
- Database Optimization: Proper indexing and query optimization
- Caching Strategy: Multiple cache layers for performance
- Progressive Enhancement: Core features work without intelligence data
- API Response Time: < 500ms for cached data, < 2s for fresh API calls
- Database Performance: Keyword queries under 100ms
- Cache Hit Rate: > 80% of keyword requests served from cache
- API Quota Efficiency: < 50% quota usage for typical user activity
- User Engagement: Increased keyword addition and management activity
- Feature Adoption: > 70% of active users accessing intelligence features
- Data Coverage: > 90% of active keywords enriched with intelligence data
- User Satisfaction: Positive feedback on keyword insights and decision-making
This enhancement transforms IndexNow Studio from a basic rank tracker into a comprehensive keyword intelligence platform. By implementing the SeRanking integration with a local keyword bank cache, users gain valuable market insights while maintaining system performance and cost efficiency.
The architecture prioritizes data freshness, API efficiency, and user experience while providing a foundation for additional intelligence providers in the future. The phased implementation approach ensures minimal disruption to existing functionality while delivering immediate value to users.
Document Status: Ready for Implementation
Technical Complexity: Medium-High
Estimated Development Time: 4 weeks
Database Changes Required: Yes (SQL queries provided)
API Integration Required: Yes (SeRanking API)
INSERT INTO "public"."indb_site_integration" ("id", "service_name", "apikey", "api_quota_limit", "api_quota_used", "quota_reset_date", "is_active", "created_at", "updated_at", "api_url") VALUES ('22f72381-c158-431c-b600-ea82752ff045', 'seranking_keyword_export', '952945a4-5d7a-4719-16cd-5e4b8b3892d6', '10000', '0', '2025-10-17', 'true', '2025-09-17 16:51:00.926251+00', '2025-09-17 16:51:00.926251+00', 'https://api.seranking.com'), ('8aee4b32-1022-4928-8d9c-ad7254a31134', 'firecrawl', 'fc-92197574a4e549799cc43d0a16c7fdde', '50674', '0', null, 'true', '2025-09-05 04:21:46.756802+00', '2025-09-15 12:35:03.451+00', 'https://api.firecrawl.dev');
INSERT INTO "public"."indb_keyword_keywords" ("id", "user_id", "domain_id", "keyword", "device_type", "country_id", "tags", "is_active", "created_at", "updated_at", "last_check_date", "keyword_bank_id", "search_volume", "cpc", "competition", "difficulty", "keyword_intent", "history_trend", "intelligence_updated_at") VALUES ('64b12962-46de-4bc2-b889-be7949b45ca9', '915f50e5-0902-466a-b1af-bdf19d789722', '9506479c-6d94-4d81-93f3-65f9180f1032', 'nexjob', 'desktop', '1650e5e6-6ed5-44b3-b253-4353714be8e6', '{}', 'true', '2025-09-05 04:25:34.513254+00', '2025-09-15 12:35:04.713+00', '2025-09-15', null, null, null, null, null, null, null, null);
INSERT INTO "public"."indb_keyword_bank" ("id", "keyword", "country_code", "language_code", "is_data_found", "volume", "cpc", "competition", "difficulty", "history_trend", "keyword_intent", "data_updated_at", "created_at", "updated_at") VALUES ('b7152871-fcf2-415c-9fca-8d56db4a24d8', 'cetta', 'us', 'en', 'true', '140', null, null, '5', '{"2024-08-01": 90, "2024-09-01": 70, "2024-10-01": 90, "2024-11-01": 90, "2024-12-01": 110, "2025-01-01": 140, "2025-02-01": 140, "2025-03-01": 140, "2025-04-01": 210, "2025-05-01": 210, "2025-06-01": 170, "2025-07-01": 210}', null, '2025-09-17 20:42:27.088+00', '2025-09-17 20:42:27.134004+00', '2025-09-17 20:42:27.134004+00'), ('d179d56b-d951-4a97-80ab-469436a03017', 'nexjob', 'us', 'en', 'true', '170', null, null, '11', '{"2024-08-01": 50, "2024-09-01": 880, "2024-10-01": 590, "2024-11-01": 90, "2024-12-01": 10, "2025-01-01": 20, "2025-02-01": 20, "2025-03-01": 30, "2025-04-01": 10, "2025-05-01": 20, "2025-06-01": 10, "2025-07-01": 30}', null, '2025-09-17 20:31:29.32+00', '2025-09-17 20:31:29.34523+00', '2025-09-17 20:31:29.34523+00');