Authentication
Public API requests must include a valid Bearer token in the Authorization header. Generate API keys from Settings > API Keys in the dashboard. Keys use the bf_mcp_ prefix and can be scoped for read, write, and AI execution access.
Base URL
https://tryroman.app
Request Header
Authorization: Bearer bf_mcp_your_api_key_here
Scopes
| Scope | Use |
|---|---|
| api:read | Read documented resources and analytics. Also covers the read-only AI Visibility API. |
| api:write | Create, update, delete, publish, and start documented non-AI operations. |
| ai:execute | Run endpoints that spend AI credits, such as generation, research, and deep analysis. |
Example: List Articles
curl -X GET https://tryroman.app/api/articles \ -H "Authorization: Bearer bf_mcp_your_api_key_here" \ -H "Content-Type: application/json"
Example: Create an Article
curl -X POST https://tryroman.app/api/articles \
-H "Authorization: Bearer bf_mcp_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"product_id": "your-product-id",
"target_keyword": "best crm for startups",
"word_range": 2500
}'Responses & Errors
Each endpoint below includes a representative success response. JSON endpoints may include additional fields as the resource grows, so clients should ignore unknown fields and key off stable identifiers, status values, and pagination fields.
Success Response Types
| Type | How to handle it |
|---|---|
| 200 OK - JSON | Parse the response body as JSON. List endpoints usually return a data array plus pagination or totals. |
| 202 Accepted - JSON | The job was queued or started. Poll the matching detail/status endpoint, or consume the event stream listed in the endpoint note. |
| text/event-stream | Read Server-Sent Events until a done, completed, failed, or error event. Reconnect only if the job is still non-terminal. |
| application/x-ndjson | Process one JSON object per line. Treat a final done line as success and an error line as a terminal failure. |
| binary file | Use the response blob or stream directly. Do not call JSON parsing on export endpoints. |
Error Response Shape
Errors return JSON. The error field is safe to show in logs or developer tools. The codefield is the best value to branch on when present.
{
"error": "API key requires the ai:execute scope for this endpoint.",
"code": "API_SCOPE_REQUIRED",
"required_scope": "ai:execute",
"required_scopes": ["api:write", "ai:execute"]
}Error Handling Guide
| Status | Retry | What to do |
|---|---|---|
| 400 | No | Fix the request payload or query parameters before sending again. |
| 401 | No | Check that the Bearer token is present, active, and not revoked. |
| 403 | No | Grant the required scope, use a dashboard session, or switch to a documented public endpoint. |
| 404 | No | Verify the resource ID and product/account ownership. |
| 409 | After state changes | Poll the active job or wait until the resource reaches a valid state. |
| 429 | Yes | Back off, honor Retry-After when present, and add jitter before retrying. |
| 500 | Yes | Retry with exponential backoff. Log the method, path, status, and response body. |
| 503 | Yes | Retry with backoff. This can happen when auth, billing, provider, or configuration dependencies are temporarily unavailable. |
Client Pattern
async function romanFetch(path, options = {}) {
const res = await fetch(`https://tryroman.app${path}`, {
...options,
headers: {
Authorization: 'Bearer bf_mcp_your_api_key_here',
'Content-Type': 'application/json',
...(options.headers || {})
}
})
if (!res.ok) {
const body = await res.json().catch(() => ({}))
const retryable = [429, 500, 503].includes(res.status)
throw { status: res.status, retryable, ...body }
}
if (res.status === 202) {
return { accepted: true, body: await res.json() }
}
return res.json()
}Webhook Publishing
Custom webhooks let Roman publish an article to any system that can receive an HTTP POST. Configure the destination in Settings > CMS > Custom Webhook, then publish articles from the dashboard.
Publish Flow
| Step | What happens |
|---|---|
| 1. Configure | Add your receiver URL and optional secret in the Custom Webhook settings. |
| 2. Trigger | Publish the article from Roman after the webhook destination has been saved. |
| 3. Send | Roman posts article content and metadata to your URL as JSON. A request body contentHtml value overrides the saved article HTML for that publish. |
| 4. Confirm | Any 2xx response is treated as success. Roman then marks the article as published and records webhook publish history. |
| 5. Fail | If your endpoint is unreachable or returns a non-2xx response, Roman returns an error to the caller and does not mark the article as published. |
Outbound Request Headers
Content-Type: application/json X-Webhook-Secret: your-secret # only included when configured
Store the same secret in your receiver and compare it with the X-Webhook-Secret header before accepting the payload.
Article Payload
{
"article_id": "uuid",
"article_type": "normal_article",
"article_subtype": "listicle",
"title": "Article headline or keyword",
"headline": "Article headline",
"cover_image_url": "https://cdn.example.com/cover.png",
"meta_title": "SEO meta title",
"meta_description": "SEO meta description",
"target_keyword": "Primary target keyword",
"content_html": "<p>Full article HTML...</p>",
"status": "complete",
"published_at": "2026-06-29T10:30:00.000Z"
}Payload Fields
| Field | Description |
|---|---|
| article_id | Unique Roman article ID. |
| article_type | ai_visibility_answer for visibility answer articles; otherwise normal_article. |
| article_subtype | Saved Roman article subtype, such as listicle, comparison, or ai_visibility_answer. May be null. |
| title | Headline when available, then meta title, then target keyword. |
| headline | Article headline, or null when unset. |
| cover_image_url | Cover image URL, or null when no cover image is set. |
| meta_title | SEO title, or null when unset. |
| meta_description | SEO description, or null when unset. |
| target_keyword | Primary keyword the article targets. |
| content_html | Full article body as HTML. |
| status | Article status at the time the webhook is triggered. |
| published_at | ISO 8601 timestamp generated when Roman sends the webhook. |
Example Receiver
export async function POST(request: Request) {
const secret = request.headers.get('X-Webhook-Secret')
if (secret !== process.env.ROMAN_WEBHOOK_SECRET) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
const article = await request.json()
await publishArticleToCms({
id: article.article_id,
title: article.title,
html: article.content_html,
coverImageUrl: article.cover_image_url,
})
return Response.json({ ok: true })
}Testing and Errors
| Case | Result |
|---|---|
| Test webhook | The settings page sends a small JSON test payload to your URL with the same secret header. |
| No URL | The publish endpoint returns WEBHOOK_NOT_CONFIGURED. |
| No content | Roman rejects the publish until the article has HTML content. |
| Receiver error | Roman returns WEBHOOK_FAILED for non-2xx responses and WEBHOOK_ERROR when the URL cannot be reached. |
Articles
Create, update, generate, publish, and export articles.
/api/articlesList articles
Retrieve a paginated list of articles with optional filters.
| Name | Type | Required | Description |
|---|---|---|---|
| page | number | No | Page number, starting at 0. |
| limit | number | No | Items per page. Default 20. |
| search | string | No | Search by target keyword. |
| statuses | string | No | Comma-separated status filter. |
| product_ids | string | No | Comma-separated product IDs. |
200 OK - JSON
{
"data": [{ "id": "...", "target_keyword": "best crm", "status": "draft" }],
"total": 42,
"hasMore": true
}/api/articlesCreate an article
Create a draft article record. Use the generation endpoints to produce the outline or content.
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | Yes | Product ID. |
| target_keyword | string | Yes | Target keyword. |
| search_query | string | No | Search query. Defaults to target_keyword. |
| custom_instructions | string | No | Extra writing instructions. |
| word_range | number | No | Target word count. |
200 OK - JSON
{
"id": "article-id",
"target_keyword": "best crm for startups",
"status": "draft"
}/api/articlesBulk update article status
| Name | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Article IDs to update. |
| status | string | Yes | New status. |
200 OK - JSON
{ "success": true, "updated": 5 }/api/articlesBulk delete articles
Permanently delete multiple articles and their associated assets.
| Name | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Article IDs to delete. |
200 OK - JSON
{ "success": true }/api/articles/:idGet an article
Retrieve article content, outline, product, and writing-style details.
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/articles/:idUpdate an article
| Name | Type | Required | Description |
|---|---|---|---|
| target_keyword | string | No | Updated keyword. |
| status | string | No | Updated status. |
| content_html | string | No | Updated HTML content. |
| outline | object | No | Updated outline. |
200 OK - JSON
{
"success": true,
"id": "...",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/articles/:idDelete an article
200 OK - JSON
{ "success": true }/api/articles/:id/generate-outlineGenerate an outline
Starts outline generation and returns 202 Accepted.
Fire-and-forget. Poll the article status or consume generation-events until a terminal event.
| Name | Type | Required | Description |
|---|---|---|---|
| stream | boolean | No | When true, response includes a progress stream URL. |
202 Accepted - JSON
{
"accepted": true,
"articleId": "...",
"stream": { "runId": "...", "url": "/api/articles/.../generation-events?runId=..." }
}/api/articles/:id/generate-contentGenerate article content
Starts content generation from an existing outline and returns 202 Accepted.
Fire-and-forget. Poll the article status or consume generation-events until a terminal event.
| Name | Type | Required | Description |
|---|---|---|---|
| keyword_ranges | object | No | SEO keyword ranges. |
| stream | boolean | No | When true, response includes a progress stream URL. |
202 Accepted - JSON
{
"accepted": true,
"id": "...",
"status": "queued"
}/api/articles/:id/generate-articleGenerate full article
Starts outline and content generation in one background run.
Fire-and-forget. Use generation-events for live progress.
| Name | Type | Required | Description |
|---|---|---|---|
| stream | boolean | No | When true, response includes a progress stream URL. |
202 Accepted - JSON
{
"accepted": true,
"id": "...",
"status": "queued"
}/api/articles/:id/generation-eventsStream article generation progress
Server-Sent Events stream for an accepted outline, content, or full-article generation run.
SSE stream. The stream also emits heartbeat events while the job is running.
| Name | Type | Required | Description |
|---|---|---|---|
| runId | string | No | Generation run ID returned by the start endpoint. |
| kind | string | No | outline or content. |
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/articles/:id/generate-linksGenerate link suggestions
Return internal and external link suggestions for an article.
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/articles/:id/generate-linkedin-postGenerate LinkedIn post draft
| Name | Type | Required | Description |
|---|---|---|---|
| customVision | string | No | Extra instructions for the post. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/articles/:id/publish-cmsPublish to connected CMS
Publishes an article through the configured CMS integration.
Returns an SSE progress stream.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/articles/:id/export-docxExport as DOCX
Returns a binary DOCX file.
| Name | Type | Required | Description |
|---|---|---|---|
| html | string | Yes | HTML content to export. |
| title | string | No | Document title. |
200 OK - DOCX file
Binary file response with Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
/api/articles/:id/assetsList article assets
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/articles/:id/assetsUpdate asset status
| Name | Type | Required | Description |
|---|---|---|---|
| assetId | string | Yes | Asset ID. |
| status | string | Yes | pending, approved, or rejected. |
200 OK - JSON
{
"success": true,
"id": "...",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/articles/:id/assetsDelete article assets
| Name | Type | Required | Description |
|---|---|---|---|
| assetId | string | No | Specific asset ID to delete. |
| type | string | No | Delete all assets of this type. |
200 OK - JSON
{ "success": true }/api/articles/bulk-writeBulk generate articles
Generate content for multiple articles.
Returns one SSE session_created acknowledgement with HTTP 202. Generation runs in a durable server worker; poll GET /api/articles/bulk-write/:sessionId for progress.
| Name | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Article IDs to process. |
| mode | string | No | outline_ready_content (default) writes Outline Ready articles; draft_full_article researches, outlines, and writes Draft articles. |
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/articles/bulk-writeList bulk write sessions
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/articles/bulk-write/:sessionIdGet bulk write session
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/articles/bulk-write/:sessionIdUpdate bulk write session
200 OK - JSON
{
"success": true,
"id": "...",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/articles/bulk-publish-cmsBulk publish to CMS
Returns an SSE stream with progress.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}Products
Manage products and product research inputs.
/api/productsList products
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/productsCreate a product
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Product name. |
| description | string | No | Product description. |
| sitemap_url | string | No | Website or sitemap URL. |
| writing_style | object | No | Initial writing style. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/products/:idGet a product
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/products/:idUpdate a product
200 OK - JSON
{
"success": true,
"id": "...",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/products/:idDelete a product
200 OK - JSON
{ "success": true }/api/products/analyze-urlAnalyze product URL
Extract product details from a website.
| Name | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Website URL. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/products/analyze-writing-styleExtract writing style
| Name | Type | Required | Description |
|---|---|---|---|
| urls | string[] | Yes | Blog URLs to analyze. Max 5. |
| productName | string | No | Product name for context. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/products/suggest-positioningSuggest positioning
| Name | Type | Required | Description |
|---|---|---|---|
| product | object | Yes | Product details. |
| answers | object | No | Optional questionnaire answers. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/products/extract-documentExtract product data from document
Send as multipart/form-data.
| Name | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | PDF, TXT, or Markdown file. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}Keywords
Research keywords, inspect SERPs, and move keywords into import batches.
/api/keywords/researchRun keyword research
| Name | Type | Required | Description |
|---|---|---|---|
| keywords | string[] | No | One to five seed keywords. Use this or seed_keyword. |
| seed_keyword | string | No | Legacy single seed keyword. Use this or keywords. |
| product_id | string | No | Product ID used to associate saved research and dedupe. |
| language_code | string | No | Language code. Defaults to en. |
| location_code | number | No | DataForSEO location code. Defaults to United States (2840). |
200 OK - JSON
{
"query_id": "...",
"status": "completed",
"data": []
}/api/keywords/research/historyList keyword research history
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/keywords/research/:queryIdGet keyword research results
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/keywords/research/add-to-importAdd keyword results to import
| Name | Type | Required | Description |
|---|---|---|---|
| result_ids | string[] | Yes | Keyword result IDs. |
| product_id | string | Yes | Product ID. |
| batch_id | string | No | Existing batch ID. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/keywords/research/serpFetch SERP competitors
| Name | Type | Required | Description |
|---|---|---|---|
| keyword | string | Yes | Keyword to check. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}Bulk Import
Create keyword batches and generate articles in bulk.
/api/bulk-importList keyword import batches
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/bulk-importCreate keyword import batch
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | Yes | Product ID. |
| keywords | object[] | Yes | Keyword rows. |
| name | string | No | Batch name. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/bulk-import/batch/:batchIdGet import batch
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/bulk-import/:idUpdate a keyword import row
200 OK - JSON
{
"success": true,
"id": "...",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/bulk-import/:idDelete a keyword import row
200 OK - JSON
{ "success": true }/api/bulk-import/generateGenerate from batch
Returns an SSE stream with progress.
| Name | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Keyword import IDs to process. |
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}Content Import
Import existing content from sitemaps or connected CMS providers.
/api/content-importList content import batches
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/content-import/batchCreate content import batch
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | Yes | Product ID. |
| source_type | string | Yes | sitemap or CMS provider. |
| sitemap_url | string | No | Sitemap URL for sitemap imports. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/content-import/batch/:batchIdGet content import batch
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/content-import/batch/:batchId/eventsStream content import progress
SSE stream.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/content-import/fetchFetch source content
Returns an SSE stream.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/content-import/importImport fetched content as articles
Returns an SSE stream.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}AI Visibility
Read-only visibility, citation, prompt, competitor, alert, and run data.
/api/ai-visibility/overviewGet AI Visibility overview
Returns aggregate metrics, trend points, provider breakdowns, top prompts to fix, alerts, and suggestions.
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. Defaults to the active product. |
| window | number | No | Lookback window in days. |
200 OK - JSON
{
"visibility_score": 72,
"share_of_voice": 0.34,
"citation_rate": 0.61,
"providers": [{ "provider": "chatgpt", "visibility_score": 76 }],
"alerts": [],
"suggestions": []
}/api/ai-visibility/settingsGet AI Visibility settings
Read the configured brand, competitors, aliases, providers, cadence, and setup state.
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. Defaults to the active product. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/promptsList tracked prompts
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| page | number | No | Page number. Default 1. |
| page_size | number | No | Page size. Max 100. |
| active | boolean | No | Set false to list inactive prompts. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/prompts/:idGet prompt detail
Returns prompt metrics, provider responses, citations, fan-out queries, competitor rows, and timeline data.
| Name | Type | Required | Description |
|---|---|---|---|
| page | number | No | Response page number. |
| page_size | number | No | Response page size. Max 50. |
| citation_page | number | No | Citation page number. |
| citation_page_size | number | No | Citation page size. Max 50. |
200 OK - JSON
{
"prompt": { "id": "...", "text": "...", "active": true },
"metrics": { "visibility_score": 72, "share_of_voice": 0.34 },
"responses": { "data": [], "page": 1, "page_size": 20 },
"citations": { "data": [], "page": 1, "page_size": 20 }
}/api/ai-visibility/citationsList AI Visibility citations
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| provider | string | No | Provider filter. |
| category | string | No | Citation category filter. |
| prompt_id | string | No | Prompt filter. |
| date_from | string | No | Start date. |
| date_to | string | No | End date. |
| view | string | No | all or own. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/competitorsList competitor profiles
Returns lightweight competitor profile summaries with AI visibility, share of voice, citation rate, citation URL count, and stable identifiers.
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| competitor | string | No | Stable competitor identifier or legacy name used to scope gap_prompts. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/competitors/:competitorGet competitor profile
Returns one competitor profile and a paginated list of citation URLs. Each URL carries its content category and a per-prompt breakdown of where it was cited. The competitor path value is the stable identifier returned by the list endpoint; legacy encoded names are also accepted.
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| page | number | No | Citation URL page number. Default 1. Clamped to the last page of the filtered set. |
| page_size | number | No | Citation URL page size. Max 100. |
| citation_category | string | No | Filter citation URLs by content category (listicle, marketing_page, comparison, …). |
| citation_provider | string | No | Filter citation URLs by AI engine. |
| citation_prompt | string | No | Filter citation URLs to those cited under one prompt id. |
| citation_search | string | No | Free-text match against citation URL and title. Max 200 chars. |
| date_from | string | No | Start date. |
| date_to | string | No | End date. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/alertsList AI Visibility alerts
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| status | string | No | Alert status filter. |
| page | number | No | Page number. |
| page_size | number | No | Page size. Max 100. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/suggestionsList AI Visibility suggestions
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| status | string | No | Suggestion status filter. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}/api/ai-visibility/runsList AI Visibility runs
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| page | number | No | Page number. |
| page_size | number | No | Page size. Max 100. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"page_size": 20,
"total": 42
}SEO Audits
Create, inspect, and process technical SEO audits.
/api/seo-auditList audits
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID filter. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/seo-auditCreate an audit
Creates an audit job and auto-starts background processing when possible.
Background job. Read GET /api/seo-audit/:id for status and page counts.
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
| sitemap_url | string | No | Website or sitemap URL. |
| urls | string[] | No | Explicit URLs to audit. |
| mode | string | No | full or lite. |
| skip_lighthouse | boolean | No | Skip Lighthouse analysis. |
202 Accepted - JSON
{
"accepted": true,
"id": "...",
"status": "queued"
}/api/seo-audit/:idGet audit details
| Name | Type | Required | Description |
|---|---|---|---|
| slim | boolean | No | Lightweight polling mode. |
| no_pages | boolean | No | Return audit metadata without page rows. |
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/seo-audit/:idDelete an audit
200 OK - JSON
{ "success": true }/api/seo-audit/:id/processStart or continue audit processing
Idempotently queues durable processing and returns persisted progress, hasMore, and generic runtime estimates.
Returns 409 with { paused: true } while paused. Poll the audit details endpoint for completion.
200 OK - JSON
{
"progress": { "total": 160, "crawled": 42, "analyzed": 37 },
"hasMore": true,
"pages": [],
"crawlRuntime": {
"phase": "crawling",
"currentUrl": "https://example.com/docs",
"processedUrls": 42,
"analyzedUrls": 37,
"totalUrls": 160,
"failedUrls": 1,
"discardedUrls": 0,
"estimatedCostUsd": 0.21,
"crawlTargetSeconds": 1080,
"crawlEtaSeconds": 708,
"analysisEtaSeconds": 738,
"summaryEtaSeconds": 60,
"overallEtaSeconds": 798,
"lastHeartbeatAt": "2026-07-15T12:00:00.000Z"
}
}/api/seo-audit/:id/summaryGet or queue the audit summary
Completed audits return the durable summary immediately. Active audits queue durable processing and return persisted progress.
Clients must handle 200, 202, and 409 responses; an active request does not synchronously generate a summary.
200 OK - JSON
// 200 — completed
{ "summary": { "overallScore": 82 }, "linkGraph": { "stats": { "totalPages": 160 } } }
// 202 — active
{ "queued": true, "progress": { "total": 160, "crawled": 42, "analyzed": 37 }, "hasMore": true }
// 409 — paused
{ "paused": true }
// 409 — failed
{ "error": "Resume, restart, or run a Deep Scan before requesting the summary." }/api/seo-audit/:id/link-analysisRun link analysis
Returns an SSE stream with progress.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/seo-audit/:id/insightsGet audit insights
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/seo-audit/:id/page/:pageIdGet audited page details
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/seo-audit/:id/page/:pageId/deep-analysisRun deep page analysis
Returns an SSE stream with progress.
200 OK - text/event-stream
event: progress
data: {"status":"running","message":"Processing","processed":12,"total":50}
event: done
data: {"status":"completed","id":"..."}/api/seo-audit/preview-sitemapPreview sitemap URLs
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | Yes | Product ID. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}Backlinks
Manage backlink sessions, research referring domains, and enrich opportunities.
/api/backlinksList backlink sessions
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | No | Product ID. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/backlinksCreate backlink session
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Session name. |
| product_id | string | Yes | Product ID. |
| csv_content | string | Yes | CSV content. |
200 OK - JSON
{
"success": true,
"id": "...",
"status": "created"
}/api/backlinks/:idGet backlink session
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/backlinks/:idDelete backlink session
200 OK - JSON
{ "success": true }/api/backlinks/:id/enrichEnrich backlink opportunities
Returns an NDJSON stream with progress updates.
| Name | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Opportunity IDs. |
200 OK - application/x-ndjson
{"type":"progress","status":"running","processed":12,"total":50}
{"type":"result","id":"...","status":"updated"}
{"type":"done","status":"completed","processed":50}/api/backlinks/researchRun backlink research
| Name | Type | Required | Description |
|---|---|---|---|
| target | string | Yes | Domain or URL. |
| query_type | string | No | backlinks, referring_domains, competitors, or domain_intersection. |
| product_id | string | No | Product ID. |
| limit | number | No | Result limit. |
200 OK - JSON
{
"query_id": "...",
"status": "completed",
"data": []
}/api/backlinks/research/historyList backlink research history
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/backlinks/research/:queryIdGet backlink research results
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}/api/backlinks/research/create-sessionCreate backlink session from research
| Name | Type | Required | Description |
|---|---|---|---|
| result_ids | string[] | Yes | Research result IDs. |
| product_id | string | Yes | Product ID. |
200 OK - JSON
{
"query_id": "...",
"status": "completed",
"data": []
}Search Console
Read cached Google Search Console performance data.
/api/gsc/connectionsList Search Console connections
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/gsc/performanceGet performance data
| Name | Type | Required | Description |
|---|---|---|---|
| product_id | string | Yes | Product ID. |
| date_from | string | No | Start date. |
| date_to | string | No | End date. |
| article_id | string | No | Article filter. |
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/gsc/top-queriesGet top search queries
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/gsc/article-analyticsGet article analytics
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/gsc/site-analyticsGet site analytics
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/gsc/pagesGet page-level performance
200 OK - JSON
{
"data": [{ "id": "...", "status": "active" }],
"page": 1,
"limit": 20,
"total": 42
}/api/gsc/agent-suggestionsGet Search Console suggestions
200 OK - JSON
{
"id": "...",
"status": "active",
"created_at": "2026-06-02T00:00:00.000Z",
"updated_at": "2026-06-02T00:00:00.000Z"
}