RankFabric Engine - Client Integration Guide
Base URL
https://rankfabric-edge.grocerybundles.workers.dev
Authentication
All requests must include an API Key in the Authorization header:
Authorization: Bearer sk_live_YOUR_API_KEY
Important: Do NOT use a Base44 proxy function. Call the RankFabric Engine directly from your React app.
React Integration Example
Setup API Client (RankFabricEngine.jsx)
import axios from 'axios';
const RANKFABRIC_API_URL = 'https://rankfabric-edge.grocerybundles.workers.dev';
// Get API key from Base44 organization settings
const getApiKey = () => {
// TODO: Store in organization settings or environment
return 'sk_live_YOUR_API_KEY';
};
export const rankFabricAPI = axios.create({
baseURL: RANKFABRIC_API_URL,
headers: {
'Authorization': `Bearer ${getApiKey()}`,
'Content-Type': 'application/json'
}
});
// API Methods
export const RankFabricEngine = {
// Track keyword - Returns instant SERP data + sets up daily tracking
async trackKeyword({ project_id, keyword_text, location_code = 2840, language_code = 'en' }) {
const response = await rankFabricAPI.post('/api/keywords/track', {
project_id,
keyword_text,
location_code,
language_code
});
return response.data;
},
// List all tracked keywords with enrichment data
async getKeywords(project_id) {
const response = await rankFabricAPI.get('/api/subscriptions', {
params: { project_id }
});
return response.data;
},
// Get unified rankings (SEO + App Store + Local)
async getRankings(project_id, filters = {}) {
const response = await rankFabricAPI.get('/api/rankings', {
params: { project_id, ...filters }
});
return response.data.rankings;
},
// List assets (websites, apps)
async listAssets(project_id) {
const response = await rankFabricAPI.get('/api/assets', {
params: { project_id }
});
return response.data.assets;
},
// Add asset
async addAsset({ project_id, type, value, name, role }) {
const response = await rankFabricAPI.post('/api/assets', {
project_id,
type, // 'website' | 'app'
value, // 'nike.com' | 'app_id'
name,
role // 'primary' | 'competitor'
});
return response.data;
},
// Portfolio trends (aggregated search volume over time)
async getPortfolioTrends(project_id, source = 'google_ads', limit = 24) {
const response = await rankFabricAPI.get('/api/keywords/portfolio-trends', {
params: { project_id, source, limit }
});
return response.data;
}
};
Key Integration Points
1. Keyword Tracking Page
Add Keyword Button:
const handleAddKeyword = async (keywordText) => {
try {
const result = await RankFabricEngine.trackKeyword({
project_id: currentProject.id,
keyword_text: keywordText,
location_code: 2840 // US
});
// Result includes instant SERP data
console.log('Top 100 results:', result.serp_data);
// Keyword is now tracked daily
refetchKeywords();
} catch (error) {
console.error('Failed to track keyword:', error);
}
};
Keyword List Table:
const { data: keywords } = useQuery({
queryKey: ['keywords', projectId],
queryFn: () => RankFabricEngine.getKeywords(projectId)
});
// Display columns:
// - keyword_text
// - search_volume (from enrichment)
// - cpc
// - competition
// - status (from subscription)
2. Rankings Dashboard
Unified Rankings Table:
const { data: rankings } = useQuery({
queryKey: ['rankings', projectId],
queryFn: () => RankFabricEngine.getRankings(projectId)
});
// Rankings include:
// - keyword (for SEO)
// - position
// - url
// - channel ('SEO', 'app_store', 'local')
// - platform ('Google', 'Bing', 'Apple', 'Google Play')
// - check_ts (timestamp)
3. Portfolio Trends
Chart showing total search volume over time:
const { data: trends } = useQuery({
queryKey: ['portfolio-trends', projectId],
queryFn: () => RankFabricEngine.getPortfolioTrends(projectId)
});
// Use trends.monthly_trends for chart:
// [{ year: 2025, month: 11, total_search_volume: 1234567 }, ...]
Data Storage Strategy
✅ DO Store in Base44 Postgres:
projectstable withid,name,created_at- User → Project relationships
- UI preferences, settings
❌ DON'T Store in Base44 Postgres:
- Keywords (query from Engine via
/api/subscriptions) - Rankings data (query from Engine via
/api/rankings) - SERP results (returned live, stored in Engine's D1)
- Search volumes, CPC, competition (stored in Engine after enrichment)
Why?
The Engine is the single source of truth for SEO/ASO data. Your Postgres should only store:
- Project metadata
- User preferences
- Organization settings
Enrichment Flow (Automatic)
When you call /api/keywords/track, the Engine automatically:
- ✅ Returns instant SERP data (Top 100)
- ✅ Creates daily tracking subscription
- ✅ Queues background enrichment job
- ✅ Fetches from DataForSEO Keyword Overview:
- 84 months of historical search volume
- Current search volume, CPC, competition
- Clickstream data (real user behavior)
- Demographics (age, gender)
- Backlink metrics
Timeline:
- Instant: SERP data (0-2 seconds)
- Background: Enrichment data (5-30 seconds)
- Daily: Updated SERP rankings (scheduled)
To check if enrichment completed:
const keywords = await RankFabricEngine.getKeywords(project_id);
// If keyword.search_volume is not null, enrichment is done
Error Handling
try {
const result = await RankFabricEngine.trackKeyword({...});
} catch (error) {
if (error.response?.status === 401) {
// Invalid or missing API key
console.error('Authentication failed');
} else if (error.response?.status === 400) {
// Bad request (missing project_id, etc)
console.error('Validation error:', error.response.data.error);
} else if (error.response?.status === 404) {
// Project not found
console.error('Project does not exist');
} else {
// Network or server error
console.error('Request failed:', error.message);
}
}
Common Issues
❌ 404 Error: /functions/rankFabricProxy
Problem: You're calling a Base44 proxy function that doesn't exist. Solution: Call the RankFabric Engine directly (see React example above).
❌ Keywords not showing search volume
Problem: Enrichment hasn't completed yet.
Solution: Wait 5-30 seconds after tracking, then refetch. Check keyword.search_volume !== null.
❌ CORS errors
Problem: Calling from wrong origin or missing CORS headers.
Solution: Ensure you're calling from https://base44.app (CORS is configured for this).
API Reference
Track Keyword
POST /api/keywords/track
{
"project_id": "proj_123",
"keyword_text": "best crm software",
"location_code": 2840,
"language_code": "en",
"device": "desktop"
}
Response:
{
"success": true,
"keywords_queued": 1,
"keywords": [{
"keyword_id": "kw_abc123",
"keyword_text": "best crm software"
}],
"serp_data": {
"items": [
{
"type": "organic",
"rank_absolute": 1,
"url": "https://example.com",
"title": "Best CRM Software 2025",
"description": "..."
}
]
}
}
Get Subscriptions (Keywords)
GET /api/subscriptions?project_id=proj_123
Response:
{
"subscriptions": [{
"id": "sub_123",
"keyword_text": "best crm software",
"search_volume": 49500,
"cpc": 17.62,
"competition": 0.06,
"keyword_difficulty": 64,
"location_code": 2840,
"status": "active",
"next_check": 1763996924315
}]
}
Get Rankings
GET /api/rankings?project_id=proj_123
Optional Filters:
?platform=google(google, bing, apple, google_play)?channel=seo(seo, app_store, local)?limit=200(default: 200)
Response:
{
"rankings": [{
"keyword_text": "best crm software",
"rank_absolute": 5,
"url": "https://yoursite.com/crm",
"domain": "yoursite.com",
"channel": "SEO",
"platform": "Google",
"relationship": "MINE",
"check_ts": 1763910524315
}]
}
Portfolio Trends
GET /api/keywords/portfolio-trends?project_id=proj_123
Parameters:
source:google_ads|bing_normalized|clickstream_normalized(default: google_ads)limit: Number of months (default: 24)
Response:
{
"summary": {
"total_keywords": 26,
"total_current_search_volume": 1234567,
"avg_current_search_volume": 47483
},
"monthly_trends": [
{
"year": 2025,
"month": 11,
"total_search_volume": 1234567
}
],
"clickstream_trends": [
{
"year": 2025,
"month": 11,
"total_clickstream_volume": 987654,
"avg_female_pct": 52.3,
"avg_male_pct": 47.7,
"avg_age_18_24_pct": 15.2
}
]
}
Next Steps
- Remove the
rankFabricProxyfunction call from your React app - Use the
RankFabricEngineclient shown above - Store API key in your organization settings (not hardcoded)
- Test with
proj_2fae47dd1fabc10e(test project that has data)