Documentation Index
Fetch the complete documentation index at: https://dev.ranked.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Agencies can build custom dashboards on top of Ranked AI’s data using the REST API for data retrieval and webhooks for real-time updates. This guide walks through the recommended architecture.
Architecture
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Your Dashboard │────>│ Ranked API │────>│ Your Database │
│ (Frontend) │ │ (Data Pull) │ │ (Cache) │
└─────────────────┘ └──────────────┘ └─────────────────┘
│
│ Webhooks
▼
┌──────────────┐
│ Your Server │
│ (Webhook │
│ Handler) │
└──────────────┘
Step 1: Discover projects
List all SEO projects for the authenticated user:
const response = await fetch('https://app.ranked.ai/api/v1/projects', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
const { data: projects } = await response.json();
// Store project IDs for subsequent calls
projects.forEach(project => {
db.upsert('projects', {
ranked_id: project.id,
name: project.name,
website: project.websiteUrl,
status: project.status,
});
});
Step 2: Initial data sync
Pull the full dataset for each project:
async function syncProject(projectId) {
const headers = { 'Authorization': `Bearer ${apiKey}` };
const base = `https://app.ranked.ai/api/v1/projects/${projectId}`;
// Fetch all data in parallel
const [keywords, audits, backlinks, prompts, content] = await Promise.all([
fetch(`${base}/rankings/keywords?limit=1000`, { headers }).then(r => r.json()),
fetch(`${base}/audits/latest`, { headers }).then(r => r.json()),
fetch(`${base}/backlinks/summary`, { headers }).then(r => r.json()),
fetch(`${base}/prompts?limit=200`, { headers }).then(r => r.json()),
fetch(`${base}/content?limit=100`, { headers }).then(r => r.json()),
]);
// Store in your database
await db.upsertKeywords(projectId, keywords.data);
await db.upsertAudit(projectId, audits.data);
await db.upsertBacklinks(projectId, backlinks.data);
await db.upsertPrompts(projectId, prompts.data);
await db.upsertContent(projectId, content.data);
}
Step 3: Set up webhooks for real-time updates
Instead of polling, subscribe to events:
// Create webhook for each project
for (const project of projects) {
await fetch('https://app.ranked.ai/api/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${writeApiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: `Dashboard - ${project.name}`,
url: 'https://your-dashboard.com/webhooks/ranked',
project_id: project.id,
events: [
'keywords.updated',
'content.status_changed',
'content.created',
'audit.completed',
'prompts.updated',
],
}),
});
}
Step 4: Handle webhook events
app.post('/webhooks/ranked', async (req, res) => {
// Verify signature first (see Webhook Security docs)
if (!verifySignature(req)) return res.status(401).send();
const { event, project_id, data } = req.body;
switch (event) {
case 'keywords.updated':
// Re-sync keyword positions from the API
const keywords = await fetch(
`https://app.ranked.ai/api/v1/projects/${project_id}/rankings/keywords?limit=1000`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
).then(r => r.json());
await db.upsertKeywords(project_id, keywords.data);
break;
case 'audit.completed':
// Fetch the latest audit results
const audit = await fetch(
`https://app.ranked.ai/api/v1/projects/${project_id}/audits/latest`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
).then(r => r.json());
await db.upsertAudit(project_id, audit.data);
break;
case 'content.status_changed':
// Update content status in your DB
await db.updateContentStatus(data.content_id, data.new_status);
break;
case 'prompts.updated':
// Refresh AI visibility data for this prompt
const prompt = await fetch(
`https://app.ranked.ai/api/v1/projects/${project_id}/prompts/${data.prompt_id}`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
).then(r => r.json());
await db.upsertPrompt(project_id, prompt.data);
break;
}
res.status(200).send('OK');
});
Recommended sync strategy
| Data | Strategy | Frequency |
|---|
| Keyword positions | Webhook keywords.updated triggers API pull | Daily (after scan completes) |
| AI visibility | Webhook prompts.updated triggers API pull | Monthly (after analysis) |
| Audit results | Webhook audit.completed triggers API pull | Monthly (after scan) |
| Content status | Webhook content.status_changed for real-time | Real-time |
| Backlinks | Scheduled API pull (no webhook yet) | Weekly cron job |
| Reports | On-demand API call | As needed |
Tips
- Use Read Only API keys for data fetching and a separate Read + Write key for webhook management
- Cache API responses in your database rather than calling the API on every page load
- The
keywords.updated webhook fires once per daily scan, not per keyword — use it as a signal to re-sync
- Implement retry logic for your API calls with exponential backoff