API Documentation
Programmatic access to SEO intelligence, AI visibility scores, content analysis, and automated optimizations.
https://seojuice.com/api/v2
Authentication
All API requests require a Bearer token in the Authorization header. Find your API token in Dashboard → Settings → API.
Every request is scoped to your organization. You can only access websites that belong to your account.
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://seojuice.com/api/v2/websites/
import requests
resp = requests.get(
"https://seojuice.com/api/v2/websites/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
data = resp.json()
const resp = await fetch("https://seojuice.com/api/v2/websites/", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await resp.json();
If authentication fails, you receive a 401 response:
{
"error": "invalid_or_missing_token",
"message": "Authorization header must be provided with format: Bearer <token>"
}
Pagination
List endpoints return paginated results. Control pagination with query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number (1-indexed) |
| page_size | integer | 10-20 | Results per page (max 100) |
Every paginated response includes a pagination object:
{
"pagination": {
"page": 1,
"page_size": 10,
"total_count": 247,
"total_pages": 25
},
"results": [...]
}
Error Handling
All errors return a consistent JSON structure:
{
"error": "not_found",
"message": "Website not found"
}
| Error Code | HTTP Status | Description |
|---|---|---|
| invalid_or_missing_token | 401 | No Authorization header or wrong scheme |
| invalid_token | 401 | Token not found in database |
| forbidden | 403 | Plan does not include this feature |
| not_found | 404 | Resource not found |
| invalid_pagination | 400 | Non-integer or out-of-range pagination params |
| missing_parameter | 400 | Required query parameter absent |
| invalid_request | 400 | Invalid JSON body |
| rate_limit_exceeded | 429 | Hourly rate limit exceeded |
Websites & Pages
/api/v2/websites/
List all websites in your organization. Returns all websites in a single array (no pagination).
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://seojuice.com/api/v2/websites/
Show response
{
"results": [
{"domain": "example.com", "created_at": "2024-01-15T10:00:00+00:00"},
{"domain": "blog.example.com", "created_at": "2024-03-22T14:30:00+00:00"}
]
}
/api/v2/websites/{domain}/
Get a single website with its latest report and key performance indicators.
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
website = resp.json()
Show response
{
"domain": "example.com",
"created_at": "2024-01-15T10:00:00+00:00",
"last_processed_at": "2024-02-10T08:30:00+00:00",
"report": {
"created_at": "2024-02-10T08:30:00+00:00",
"data": {}
},
"kpis": {
"total_links": 1842,
"total_pages": 234,
"total_keywords": 567
}
}
/api/v2/websites/{domain}/pages/
List all pages for a website. Each page includes its internal links. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page number (default: 1) |
| page_size | integer | No | Results per page (default: 10, max: 100) |
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 234, "total_pages": 12},
"results": [
{
"url": "https://example.com/blog/seo-tips",
"created_at": "2024-01-20T12:00:00+00:00",
"last_processed_at": "2024-02-08T14:00:00+00:00",
"links": [
{
"page_from": "https://example.com/blog/seo-tips",
"page_to": "https://example.com/about",
"keyword": "learn more about us",
"created_at": "2024-01-20T12:00:00+00:00",
"impressions": 42
}
]
}
]
}
/api/v2/websites/{domain}/pages/{page_id}/
Get a single page with its internal links.
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://seojuice.com/api/v2/websites/example.com/pages/4521/
/api/v2/websites/{domain}/links/
List all SEOJuice-generated internal links for a website. Paginated.
Show response
{
"pagination": {"page": 1, "page_size": 50, "total_count": 1842, "total_pages": 37},
"results": [
{
"page_from": "https://example.com/blog/seo-tips",
"page_to": "https://example.com/about",
"keyword": "learn more about us",
"created_at": "2024-01-20T12:00:00+00:00",
"impressions": 42
}
]
}
Intelligence
/api/v2/websites/{domain}/intelligence/
Composite SEO intelligence summary including SEO score, AISO score, page counts, cluster counts, link metrics, and content gap count.
| Parameter | Type | Required | Description |
|---|---|---|---|
| period | string | No |
7d, 30d, or 90d (default: 30d)
|
| include_history | boolean | No | Include time-series snapshot data |
| include_trends | boolean | No | Include period-over-period deltas |
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/intelligence/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"period": "30d", "include_trends": "true"},
)
intel = resp.json()
Show response
{
"domain": "example.com",
"seo_score": 78,
"aiso_score": 62,
"total_pages": 234,
"total_clusters": 12,
"total_internal_links": 1842,
"total_seojuice_links": 3,
"orphan_pages": 17,
"content_gaps": 45,
"last_crawled_at": "2024-02-10T08:30:00+00:00",
"trends": {
"seo_score_change": 6.0,
"pages_change": 12,
"clicks_change_pct": 23.3,
"impressions_change_pct": 13.3
}
}
Topology
/api/v2/websites/{domain}/topology/
Internal link graph analysis. Returns orphan pages, link depth distribution, and most-linked pages.
Show response
{
"total_pages": 234,
"total_internal_links": 1842,
"orphan_pages_count": 17,
"orphan_pages": [{"url": "https://example.com/orphan-page", "title": "Orphan Page Title"}],
"link_depth_distribution": {"0": 1, "1": 23, "2": 89, "3": 64, "4+": 57},
"avg_links_per_page": 7.9,
"most_linked_pages": [{"url": "https://example.com/", "title": "Home", "incoming_links": 234}]
}
Page Speed
/api/v2/websites/{domain}/pagespeed/
Core Web Vitals and Lighthouse scores for a specific page URL.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Full URL of the page to check |
Show response
{
"url": "https://example.com/blog/seo-tips",
"loading_time": 1.24,
"core_web_vitals": {"lcp": 2.1, "cls": 0.05, "fid": 12, "inp": 45, "fcp": 1.3, "ttfb": 0.4},
"scores": {"performance": 87, "accessibility": 94, "best_practices": 92, "seo": 96},
"resource_sizes": {"total_kb": 1240, "js_kb": 480, "css_kb": 120, "image_kb": 580},
"measured_at": "2024-02-10T08:30:00+00:00"
}
Clusters
/api/v2/websites/{domain}/clusters/
List topic clusters identified by semantic analysis. Clusters group related pages by content similarity. Paginated.
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 12, "total_pages": 1},
"results": [{
"id": 42, "name": "SEO Guides", "slug": "seo-guides", "page_count": 18,
"avg_relevance_score": 0.85, "total_clicks": 4200, "avg_position": 12.3,
"traffic_trend": "growing", "internal_link_density": 0.73, "orphan_pages": 2
}]
}
/api/v2/websites/{domain}/clusters/{cluster_id}/
Cluster detail with top keywords, time-series performance data, and quality history.
Content Gaps
/api/v2/websites/{domain}/content-gaps/
Content opportunities identified by AI analysis of competitor rankings, search volumes, and AI citation gaps.
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | No | Filter by content category |
| intent | string | No |
informational, transactional, navigational, or commercial
|
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 45, "total_pages": 3},
"results": [{
"id": 101, "page_name": "How to Build Internal Links", "category": "Technical SEO",
"intent": "informational", "seo_potential": 87, "total_search_volume": 12400,
"keywords": [{"keyword": "internal linking strategy", "rank_potential": 3, "color": "green"}],
"aiso_driven": false, "is_generated": false, "has_potential_candidate": true
}]
}
Competitors
/api/v2/websites/{domain}/competitors/
Competitor analysis with keyword overlap, traffic estimates, and ranking comparisons.
| Parameter | Type | Required | Description |
|---|---|---|---|
| include_trends | boolean | No | Include period-over-period trend data |
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 8, "total_pages": 1},
"results": [{
"id": 55, "domain": "competitor.com", "score": 82, "intersections": 145,
"estimated_traffic": 52000, "content_gaps_count": 23, "avg_position": 15.4,
"top_keywords": [{"keyword": "seo tools", "your_position": 8, "their_position": 3, "volume": 33100}],
"trends": {"intersections_change": 12, "traffic_change_pct": 5.3, "keywords_change": 8}
}]
}
AISO — AI Search Visibility
/api/v2/websites/{domain}/aiso/
AI Search Optimization score. Tracks how often your site is cited by ChatGPT, Claude, Perplexity, and Gemini. The composite score is built from five sub-dimensions: visibility, sentiment, position, coverage, and competitive standing.
| Parameter | Type | Required | Description |
|---|---|---|---|
| period | string | No |
7d, 30d, or 90d (default: 30d)
|
| include_history | boolean | No | Include 12-month snapshot history |
Show response
{
"aiso_score": 62,
"sub_scores": {"visibility": 70, "sentiment": 65, "position": 58, "coverage": 55, "competitive": 62},
"total_mentions": 284, "your_mentions": 48, "avg_position": 3.2, "positive_rate": 0.79,
"providers": {},
"history": {
"months": ["2024-03", "2024-04", "2024-05"],
"aiso_scores": [55, 58, 62],
"total_mentions": [210, 245, 284],
"your_mentions": [35, 41, 48]
}
}
Semantic Search
/api/v2/websites/{domain}/similar/
Find semantically similar pages using vector embeddings. Powered by Pinecone. Results cached for 1 hour.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Full URL of the source page |
| limit | integer | No | Number of results (default: 10, max: 50) |
Show response
{
"source": {"url": "https://example.com/blog/seo-tips", "title": "10 SEO Tips for 2024"},
"similar_pages": [
{"url": "https://example.com/blog/link-building", "title": "Link Building Guide", "similarity": 0.923, "cluster": "SEO Guides"}
]
}
Page Analysis
/api/v2/websites/{domain}/analyze/
Trigger an asynchronous on-demand analysis for a page. Returns immediately with an analysis ID to poll.
curl -X POST \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/blog/seo-tips"}' \
https://seojuice.com/api/v2/websites/example.com/analyze/
Show response — 202 Accepted
{
"analysis_id": "abc123XYZ_randomtoken",
"url": "https://example.com/blog/seo-tips",
"status": "queued",
"status_url": "/api/v2/websites/example.com/analyze/abc123XYZ_randomtoken/",
"estimated_time_seconds": 30
}
/api/v2/websites/{domain}/analyze/{analysis_id}/
Poll the status of an async page analysis. Status: queued → in_progress → completed | failed
Show response — completed
{
"analysis_id": "abc123XYZ_randomtoken", "status": "completed",
"url": "https://example.com/blog/seo-tips", "page_id": 4521,
"cluster": {"id": 42, "name": "SEO Guides"}, "seo_score": 78,
"is_orphan": false, "depth_from_homepage": 2,
"recommended_links": [], "recommended_meta": {},
"recommended_structured_data": {}, "aiso_visibility": 0,
"completed_at": "2024-02-10T08:30:45+00:00"
}
Keywords
/api/v2/websites/{domain}/keywords/
List all tracked keywords for a website. Includes search volume, difficulty, CPC, and optional SERP feature data. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | No | Filter by keyword category |
| include_serp | string | No |
true or false — include SERP feature data (AI Overview, Featured Snippet, PAA, etc.)
|
| page | integer | No | Page number (default: 1) |
| page_size | integer | No | Results per page (default: 20, max: 100) |
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/keywords/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"include_serp": "true", "page_size": 50},
)
keywords = resp.json()
Show response
{
"pagination": {"page": 1, "page_size": 50, "total_count": 567, "total_pages": 12},
"results": [
{
"id": 1234,
"name": "internal linking strategy",
"page_url": "https://example.com/blog/internal-links",
"category": "Technical SEO",
"search_volume": 12400,
"keyword_difficulty": 42.5,
"cpc": 3.20,
"competition": 0.65,
"ai_search_volume": 850,
"last_updated": "2024-02-10T08:30:00+00:00"
}
]
}
/api/v2/websites/{domain}/pages/{page_id}/keywords/
Google Search Console keywords for a specific page. Each keyword includes its latest performance stats (clicks, impressions, CTR, rank). Paginated.
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://seojuice.com/api/v2/websites/example.com/pages/4521/keywords/
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 48, "total_pages": 3},
"results": [
{
"id": 5678,
"keyword": "seo tips for beginners",
"processed_at": "2024-02-10T08:30:00+00:00",
"stats": {
"clicks": 320,
"impressions": 8400,
"ctr": 0.038,
"rank": 4.2,
"created_at": "2024-02-10T00:00:00+00:00"
}
}
]
}
/api/v2/websites/{domain}/pages/{page_id}/search-stats/
Daily Google Search Console performance stats for a page. Returns time-series data for clicks, impressions, CTR, and average rank. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| period | string | No |
7d, 30d, or 90d (default: 30d)
|
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 30, "total_pages": 2},
"results": [
{
"date": "2024-02-10",
"clicks": 45,
"impressions": 1200,
"ctr": 0.0375,
"rank": 6.8
},
{
"date": "2024-02-09",
"clicks": 38,
"impressions": 1150,
"ctr": 0.033,
"rank": 7.1
}
]
}
/api/v2/websites/{domain}/pages/{page_id}/metrics-history/
Historical snapshots of page-level metrics over time. Tracks SEO score, on-page score, accessibility score, content quality, GEO readiness, Core Web Vitals, and GSC performance. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| period | string | No |
7d, 30d, or 90d (default: 90d)
|
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 90, "total_pages": 5},
"results": [
{
"created_at": "2024-02-10T08:30:00+00:00",
"seo_score": 78.5,
"onpage_score": 82.0,
"accessibility_score": 91.0,
"word_count": 2450,
"gsc_clicks": 320,
"gsc_impressions": 8400,
"gsc_avg_position": 4.2,
"gsc_ctr": 0.038,
"is_orphan": false,
"total_incoming_links": 12,
"total_outgoing_links": 8,
"cwv_lcp": 2.1,
"cwv_cls": 0.05,
"cwv_fid": 12,
"cwv_inp": 45,
"cwv_fcp": 1.3,
"cwv_ttfb": 0.4,
"cwv_performance_score": 87,
"content_quality_score": 72.0,
"geo_readiness_score": 65.0
}
]
}
Backlinks
/api/v2/websites/{domain}/backlinks/
List external backlinks pointing to your website. Filter by status or dofollow attribute. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No |
active or lost (note: the response returns capitalised values like Active)
|
| dofollow | string | No |
true or false
|
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
"https://seojuice.com/api/v2/websites/example.com/backlinks/?status=active&dofollow=true"
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 342, "total_pages": 18},
"results": [
{
"id": 891,
"source_url": "https://blog.referrer.com/seo-roundup",
"target_url": "https://example.com/blog/seo-tips",
"anchor_text": "great SEO tips",
"dofollow": true,
"nofollow": false,
"status": "Active",
"link_type": "Anchor",
"page_from_rank": 45,
"is_new": false,
"is_lost": false,
"first_discovered_at": "2024-01-15T10:00:00+00:00",
"last_crawled_at": "2024-02-08T14:00:00+00:00"
}
]
}
/api/v2/websites/{domain}/backlink-domains/
List referring domains linking to your website. Includes domain rank, spam score, country, and platform. Paginated.
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/backlink-domains/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
domains = resp.json()
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 89, "total_pages": 5},
"results": [
{
"id": 234,
"domain": "blog.referrer.com",
"rank": 45,
"spam_score": 2.1,
"country": "US",
"platform": "WordPress",
"tld": "com"
}
]
}
Quality & SERP
/api/v2/websites/{domain}/pages/{page_id}/content/
Extracted page content including cleaned text, heading hierarchy, structured data (JSON-LD), and image inventory with alt text status.
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://seojuice.com/api/v2/websites/example.com/pages/4521/content/
Show response
{
"id": 4521,
"url": "https://example.com/blog/seo-tips",
"title": "10 SEO Tips for 2024",
"meta_description": "Learn the top SEO strategies to boost your rankings in 2024.",
"content": "Search engine optimization remains one of the most effective...",
"word_count": 2450,
"headings": [
{"level": 1, "text": "10 SEO Tips for 2024"},
{"level": 2, "text": "1. Focus on Search Intent"},
{"level": 2, "text": "2. Build Topic Clusters"}
],
"structured_data": {
"@type": "Article",
"headline": "10 SEO Tips for 2024"
},
"has_structured_data": true,
"images": [
{"url": "https://example.com/images/seo-chart.png", "alt_text": "SEO growth chart", "has_alt": true},
{"url": "https://example.com/images/hero.jpg", "alt_text": null, "has_alt": false}
],
"language_code": "en",
"page_type": "blog_post"
}
/api/v2/websites/{domain}/pages/{page_id}/content-quality/
CORE-EEAT content quality audit. Scores content across four dimensions: Contextual clarity, Organization, Referenceability, and Depth of expertise. Returns per-dimension scores, issues, and top improvement recommendations.
| Parameter | Type | Required | Description |
|---|---|---|---|
| full_audit | string | No |
true to recompute scores live from raw content (slower but includes detailed issues)
|
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/pages/4521/content-quality/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"full_audit": "true"},
)
quality = resp.json()
Show response
{
"page_id": 4521,
"page_url": "https://example.com/blog/seo-tips",
"page_type": "blog_post",
"content_quality_score": 72.0,
"breakdown": {
"contextual_clarity": 80,
"organization": 75,
"referenceability": 60,
"depth_expertise": 68
},
"issues": [
{
"dimension": "referenceability",
"item": "No external citations or data sources",
"points_lost": 12,
"recommendation": "Add 2-3 authoritative external references to support key claims"
}
],
"veto_applied": null,
"top_improvements": [
{
"dimension": "referenceability",
"item": "No external citations or data sources",
"points_lost": 12,
"recommendation": "Add 2-3 authoritative external references to support key claims"
}
],
"geo_readiness_score": 65.0,
"geo_readiness_breakdown": {
"direct_answer": 70,
"faq_coverage": 55,
"structured_content": 80,
"schema_markup": 60,
"citation_density": 45,
"summary_boxes": 50
}
}
/api/v2/websites/{domain}/pages/{page_id}/geo-readiness/
GEO (Generative Engine Optimization) readiness audit. Measures how well a page is optimized for AI-generated answers across six elements: direct answers, FAQ coverage, structured content, schema markup, citation density, and summary boxes.
| Parameter | Type | Required | Description |
|---|---|---|---|
| full_audit | string | No |
true to recompute scores live from raw content (slower)
|
Show response
{
"page_id": 4521,
"page_url": "https://example.com/blog/seo-tips",
"page_type": "blog_post",
"geo_readiness_score": 65.0,
"breakdown": {
"direct_answer": 70,
"faq_coverage": 55,
"structured_content": 80,
"schema_markup": 60,
"citation_density": 45,
"summary_boxes": 50
},
"recommendations": [
{"element": "citation_density", "priority": 1, "action": "Add inline citations to support key claims"},
{"element": "faq_coverage", "priority": 2, "action": "Add an FAQ section addressing common questions"}
],
"quotable_patterns_found": {
"definitions": 2,
"statistics": 1,
"lists": 4,
"tables": 0,
"how_to_steps": 0
},
"ai_engine_tips": {
"chatgpt": "Add more structured data to improve snippet extraction",
"perplexity": "Include inline citations for better source attribution",
"gemini": "Improve FAQ coverage for conversational queries"
},
"content_quality_score": 72.0
}
/api/v2/websites/{domain}/domain-health/
CITE domain health audit. Scores your domain across four trust dimensions: Citation authority, Identity signals, Trust factors, and Eminence indicators. Includes signal-level insights and prioritized recommendations.
| Parameter | Type | Required | Description |
|---|---|---|---|
| recompute | string | No |
true to recalculate scores from latest data (slower)
|
| domain_type | string | No |
Adjusts scoring weights. One of: content_publisher, ecommerce, saas, agency, local_business, default
|
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/domain-health/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"domain_type": "saas"},
)
health = resp.json()
Show response
{
"domain": "example.com",
"domain_health_score": 74.0,
"breakdown": {
"citation": 82,
"identity": 70,
"trust": 68,
"eminence": 76
},
"insights": [
{"dimension": "citation", "signal": "referring_domains", "value": 89, "score": 85},
{"dimension": "trust", "signal": "ssl_certificate", "value": true, "score": 100},
{"dimension": "identity", "signal": "structured_org_data", "value": false, "score": 0}
],
"veto_applied": null,
"recommendations": [
{"dimension": "identity", "priority": 1, "action": "Add Organization structured data to your homepage"},
{"dimension": "trust", "priority": 2, "action": "Improve page load times across the site"}
]
}
/api/v2/websites/{domain}/serp-landscape/
SERP feature landscape for your tracked keywords. Shows which SERP features (AI Overviews, Featured Snippets, PAA, Knowledge Graph, Local Pack) appear for your keywords, your position distribution, and snippet capture opportunities.
| Parameter | Type | Required | Description |
|---|---|---|---|
| market | string | No |
Market/locale code (e.g. en_US). Defaults to website’s target market.
|
Show response
{
"feature_counts": {
"ai_overview": 45,
"featured_snippet": 23,
"people_also_ask": 156,
"knowledge_graph": 12,
"local_pack": 8
},
"total_with_snapshots": 320,
"keywords_data": [
{
"keyword": "internal linking strategy",
"search_volume": 12400,
"user_position": 3,
"has_ai_overview": true,
"has_featured_snippet": true,
"has_people_also_ask": true,
"has_knowledge_graph": false,
"has_local_pack": false,
"snapshot_date": "2024-02-10T08:30:00+00:00"
}
],
"user_position_distribution": {
"top_3": 18,
"top_10": 67,
"top_20": 124,
"not_found": 111
},
"snippet_opportunities": [
{
"keyword": "internal linking strategy",
"position": 3,
"search_volume": 12400
}
]
}
Accessibility
/api/v2/websites/{domain}/accessibility-issues/
WCAG accessibility issues detected across your website. Filter by severity, category, or auto-fixable status. Issues include WCAG criterion references, fix guidance, and the offending HTML snippet. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| severity | string | No |
Filter by severity: critical, major, or minor
|
| category | string | No | Filter by issue category |
| auto_fixable | string | No |
true or false
|
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
"https://seojuice.com/api/v2/websites/example.com/accessibility-issues/?severity=critical"
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 34, "total_pages": 2},
"results": [
{
"id": 456,
"page_url": "https://example.com/contact",
"category": "Forms",
"severity": "critical",
"wcag_criterion": "1.3.1",
"description": "Form input missing associated label",
"fix_guidance": "Add a
Changes & Decay
/api/v2/websites/{domain}/changes/
Automation change records. Tracks proposed and applied changes (internal links, meta descriptions, title tags, etc.) with status and confidence score. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No |
pending, approved, applied, pulled, verified, rejected, reverted, or expired
|
| change_type | string | No |
Filter by type (e.g. internal_link, meta_description, title_tag)
|
| url | string | No | Filter changes for a specific page URL |
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/changes/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
params={"status": "pending"},
)
changes = resp.json()
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 156, "total_pages": 8},
"results": [
{
"id": 789,
"change_type": "internal_link",
"status": "pending",
"page_url": "https://example.com/blog/seo-tips",
"proposed_value": "<a href=\"/blog/link-building\">link building guide</a>",
"previous_value": null,
"reason": "High semantic similarity (0.92) between source and target pages",
"confidence_score": 0.95,
"anchor_text": "link building guide",
"created_at": "2024-02-08T14:00:00+00:00",
"reviewed_at": null,
"applied_at": null,
"pulled_at": null,
"pulled_by_integration": null,
"verified_at": null,
"reverted_at": null,
"revert_reason": null
}
]
}
/api/v2/websites/{domain}/changes/{change_id}/
Get a single change record by ID with full details including alternatives, optimization techniques, and LLM metadata.
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/changes/789/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
change = resp.json()
/api/v2/websites/{domain}/changes/{change_id}/approve/
Approve a pending change. The change will be queued for deployment based on the automation mode. Returns 400 if the change is not in pending status.
resp = requests.post(
"https://seojuice.com/api/v2/websites/example.com/changes/789/approve/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
approved = resp.json()
/api/v2/websites/{domain}/changes/{change_id}/reject/
Reject a pending change with an optional reason. Returns 400 if the change is not in pending status.
resp = requests.post(
"https://seojuice.com/api/v2/websites/example.com/changes/789/reject/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"reason": "Not relevant to this page's topic"},
)
rejected = resp.json()
/api/v2/websites/{domain}/changes/{change_id}/revert/
Revert an applied or approved change. Rolls back the change and restores the previous value. Optionally include a reason.
resp = requests.post(
"https://seojuice.com/api/v2/websites/example.com/changes/789/revert/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"reason": "Caused layout issues on mobile"},
)
reverted = resp.json()
/api/v2/websites/{domain}/changes/{change_id}/pull/
Mark an applied change as pulled by your integration. Used in headless CMS workflows where your system fetches changes and applies them externally. Only changes in applied status can be pulled.
resp = requests.post(
"https://seojuice.com/api/v2/websites/example.com/changes/789/pull/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"integration": "contentful"},
)
pulled = resp.json()
/api/v2/websites/{domain}/changes/{change_id}/verify/
Mark a pulled change as verified after deployment. Confirms the change was successfully deployed to production. Only changes in pulled status can be verified.
resp = requests.post(
"https://seojuice.com/api/v2/websites/example.com/changes/789/verify/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
verified = resp.json()
/api/v2/websites/{domain}/changes/bulk/
Perform bulk actions on multiple change records. Supports approve, reject, revert, pull, and verify. Maximum 500 IDs per request.
| Field | Type | Required | Description |
|---|---|---|---|
| action | string | Yes |
approve, reject, revert, pull, or verify
|
| ids | int[] | Yes | Array of change record IDs (max 500) |
| reason | string | No | Reason for reject/revert actions |
| integration | string | No | Integration name for pull/verify actions |
resp = requests.post(
"https://seojuice.com/api/v2/websites/example.com/changes/bulk/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={"action": "approve", "ids": [789, 790, 791]},
)
result = resp.json()
Show response
{
"action": "approve",
"succeeded": [789, 790],
"failed": [{"id": 791, "error": "Cannot approve change in status 'applied'"}],
"total_succeeded": 2,
"total_failed": 1
}
/api/v2/websites/{domain}/changes/stats/
Get aggregated change statistics broken down by status and change type.
resp = requests.get(
"https://seojuice.com/api/v2/websites/example.com/changes/stats/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
stats = resp.json()
Show response
{
"by_status": {"pending": 42, "approved": 15, "applied": 89, "pulled": 12, "verified": 8, "rejected": 5, "reverted": 3, "expired": 2},
"by_type": {"internal_link": 95, "meta_description": 30, "title_tag": 18, "image_alt": 12, "structured_data": 8, "og_title": 5, "accessibility": 4, "local_schema": 2, "nap_fix": 2}
}
/api/v2/websites/{domain}/changes/settings/
Get or update automation settings. Controls which change types are automated, daily budgets, and path exclusions. Each change type mode can be off, suggest, manual_deploy, or auto_deploy.
resp = requests.patch(
"https://seojuice.com/api/v2/websites/example.com/changes/settings/",
headers={"Authorization": "Bearer YOUR_API_TOKEN"},
json={
"internal_links_mode": "auto_deploy",
"max_changes_per_day": 50,
"exclude_paths": "/admin/*,/staging/*",
},
)
settings = resp.json()
Show response
{
"internal_links_mode": "auto_deploy",
"meta_tags_mode": "suggest",
"og_tags_mode": "suggest",
"title_tags_mode": "suggest",
"structured_data_mode": "off",
"image_alt_mode": "suggest",
"accessibility_mode": "suggest",
"local_seo_mode": "off",
"gbp_review_reply_mode": "off",
"max_changes_per_page_per_day": 5,
"max_changes_per_day": 50,
"exclude_paths": "/admin/*,/staging/*"
}
/api/v2/websites/{domain}/content-decay/
Content decay alerts for pages losing traffic or rankings. Detects traffic drops, position losses, and combined declines. Includes baseline vs. current metrics and actionable suggestions. Paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| is_active | string | No |
true for active alerts, false for resolved
|
| severity | string | No |
critical, warning, or recovering
|
| decay_type | string | No |
traffic, position, or combined
|
Show response
{
"pagination": {"page": 1, "page_size": 20, "total_count": 12, "total_pages": 1},
"results": [
{
"id": 101,
"page_url": "https://example.com/blog/old-seo-guide",
"severity": "critical",
"decay_type": "combined",
"clicks_baseline": 450,
"clicks_current": 120,
"clicks_change_pct": -73.3,
"impressions_baseline": 12000,
"impressions_current": 4500,
"impressions_change_pct": -62.5,
"position_baseline": 4.2,
"position_current": 14.8,
"position_change": 10.6,
"is_active": true,
"detected_at": "2024-02-01T00:00:00+00:00",
"resolved_at": null,
"suggestions": [
{"action": "Update outdated statistics and references", "impact": "high"},
{"action": "Add new sections covering recent developments", "impact": "medium"}
],
"geo_suggestions": [
{
"element": "citation_density",
"current_score": 30,
"action": "Add inline citations to support key claims",
"detail": "Pages with citation density below 40 are rarely cited by AI engines",
"impact": "high"
}
]
}
]
}
Reports
Plan requirement: Report endpoints require a Startup plan or above. Free tier organizations receive a 403 Forbidden response.
/api/v2/websites/{domain}/reports/
List all generated reports for a website. Paginated, ordered by creation date descending.
Show response
{
"pagination": {"page": 1, "page_size": 12, "total_count": 5, "total_pages": 1},
"results": [{
"id": 7, "type": "last_month", "type_display": "Last Month",
"status": "completed", "date": "2024-01-01", "end_date": "2024-01-31",
"created_at": "2024-02-01T08:00:00+00:00", "has_pdf": true
}]
}
/api/v2/websites/{domain}/reports/
Generate a new SEO report. Returns immediately — poll the status URL for completion.
| Body field | Type | Required | Description |
|---|---|---|---|
| type | string | Yes |
this_month, last_month, this_week, or last_week
|
curl -X POST \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type": "last_month"}' \
https://seojuice.com/api/v2/websites/example.com/reports/
Show response — 202 Accepted
{
"report_id": 8, "status": "in_progress",
"status_url": "/api/v2/websites/example.com/reports/8/",
"task_id": "celery-task-uuid-here"
}
/api/v2/websites/{domain}/reports/{report_id}/
Report detail with full summary data and PDF download URL.
Show response
{
"id": 7, "type": "last_month", "type_display": "Last Month",
"status": "completed", "date": "2024-01-01", "end_date": "2024-01-31",
"created_at": "2024-02-01T08:00:00+00:00", "has_pdf": true,
"summary": {}, "data": {},
"updated_at": "2024-02-01T08:05:00+00:00",
"pdf_url": "/api/v2/websites/example.com/reports/7/pdf/"
}
/api/v2/websites/{domain}/reports/{report_id}/pdf/
Download the report as a PDF file. Returns binary application/pdf.
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
-o report.pdf \
https://seojuice.com/api/v2/websites/example.com/reports/7/pdf/
Google Business Profile
/api/v2/websites/{domain}/gbp/locations/
List all active Google Business Profile locations linked to the website.
Show response
{
"results": [{
"id": 3, "location_id": "accounts/123/locations/456",
"name": "SEOJuice HQ", "address": "123 Main St, San Francisco, CA",
"phone": "+1-555-555-5555", "average_rating": 4.7, "total_reviews": 89,
"last_fetched_at": "2024-02-10T06:00:00+00:00"
}]
}
/api/v2/websites/{domain}/gbp/reviews/
Paginated list of Google Business Profile reviews with filtering options. Includes AI-generated reply suggestions.
| Parameter | Type | Required | Description |
|---|---|---|---|
| rating | integer | No | Filter by star rating (1-5) |
| sentiment | string | No |
positive, negative, or neutral
|
| needs_attention | boolean | No | Only unanswered or flagged reviews |
| location_id | integer | No | Filter to a specific location |
Show response
{
"results": [{
"id": 201, "review_id": "AHEIO2fabc123xyz",
"location_name": "SEOJuice HQ", "author_name": "Jane Smith",
"rating": 5, "comment": "Great service, very helpful!",
"reply": null, "reply_suggestion": "Thank you for your kind words, Jane!",
"sentiment": "positive", "needs_attention": false, "auto_replied": false,
"published_at": "2024-02-08T14:00:00+00:00", "reply_posted_at": null
}],
"pagination": {"page": 1, "page_size": 20, "total_count": 89, "total_pages": 5}
}
/api/v2/websites/{domain}/gbp/reviews/{review_id}/reply/
Post a reply to a Google Business Profile review. Published directly to Google.
| Body field | Type | Required | Description |
|---|---|---|---|
| reply_text | string | Yes | The reply text to publish |
curl -X POST \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reply_text": "Thank you for your kind review, Jane!"}' \
https://seojuice.com/api/v2/websites/example.com/gbp/reviews/201/reply/
Show response
{"success": true, "review_id": 201, "reply": "Thank you for your kind review, Jane!"}
MCP Server
SEOJuice provides a Model Context Protocol (MCP) server for AI assistant integration. Connect Claude Desktop, Cursor, Windsurf, or any MCP-compatible client to access SEO intelligence tools directly from your AI workflow.
Endpoint: https://seojuice.com/mcp
Transport: Streamable HTTP (JSON-RPC 2.0)
Authentication: OAuth 2.0 + PKCE (automatic via discovery)
Quick Setup
Add SEOJuice to your MCP client configuration. OAuth authentication is handled automatically — your client will open a browser for login on first connection.
{
"mcpServers": {
"seojuice": {
"url": "https://seojuice.com/mcp",
"transport": "streamable-http"
}
}
}
Available Tools
All tools are read-only and scoped to your organization. Use list_websites first to discover available domains.
| Tool | Description | Parameters |
|---|---|---|
| list_websites | List all tracked websites | none |
| get_website_detail | Website KPIs (pages, links, keywords) | domain |
| list_pages | Paginated pages with internal links | domain, page, page_size |
| get_page_detail | Single page detail with links | domain, page_id |
| get_intelligence_summary | SEO score, AISO score, trends, history | domain, period, include_history, include_trends |
| get_site_topology | Link graph, orphan pages, depth distribution | domain |
| get_pagespeed | Core Web Vitals and Lighthouse scores | domain, url |
| list_clusters | Content clusters with page counts | domain, page, page_size |
| get_cluster_detail | Single cluster with time series | domain, cluster_id |
| list_content_gaps | Content gap opportunities | domain, category, intent, page, page_size |
| list_competitors | Competitor keyword overlap and trends | domain, include_trends, page, page_size |
OAuth Discovery
MCP clients discover OAuth configuration automatically. These endpoints are public (no authentication required):
| Endpoint | Purpose |
|---|---|
| GET /.well-known/oauth-protected-resource | RFC 9728 — resource metadata, authorization server URLs, supported scopes |
| GET /.well-known/oauth-authorization-server | RFC 8414 — authorization, token, and JWKS endpoints |
Security
- All data is scoped to the authenticated user's organization
- All tools are read-only — no data modifications possible
- OAuth 2.0 + PKCE ensures secure token exchange without client secrets
- JWT tokens are validated against Auth0 JWKS with signature verification
Webhooks
Receive real-time notifications when events happen in your SEOJuice account. Configure webhook endpoints in Dashboard → Settings → Webhooks.
Delivery details: Webhooks are sent as POST requests with a JSON body. Failed deliveries are retried up to 3 times at 60 s, 300 s, and 1800 s intervals.
Request Headers
| Header | Description |
|---|---|
| X-SEOJuice-Event |
Event type, e.g. report.completed
|
| X-SEOJuice-Signature | HMAC-SHA256 hex digest of the raw request body |
| X-SEOJuice-Delivery-ID | Unique UUID for this delivery attempt |
| Content-Type |
application/json
|
Signature Verification
Every webhook includes an X-SEOJuice-Signature header. Compute the HMAC-SHA256 of the raw request body using your webhook secret and compare:
import hmac
import hashlib
def verify_signature(payload_body: bytes, secret: str, signature: str) -> bool:
expected = hmac.new(
secret.encode(), payload_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
const crypto = require("crypto");
function verifySignature(payloadBody, secret, signature) {
const expected = crypto
.createHmac("sha256", secret)
.update(payloadBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
Event Reference
| Event | Trigger | Key Payload Fields |
|---|---|---|
| report.completed | Report generation finishes |
report_id, type, json_url, pdf_url
|
| report.failed | Report generation fails |
report_id, error_message
|
| cluster.updated | Cluster pages change |
cluster_id, name, page_count_change
|
| content_gap.found | New content gap detected |
gap_id, page_name, seo_potential
|
| aiso.score_changed | AISO score shifts ≥10 points |
old_score, new_score, change
|
| gbp.review_received | New Google Business review |
review_id, rating, author, comment, sentiment
|
| gbp.auto_reply_posted | Automatic reply sent |
review_id, rating, reply
|
| change.created | New change record proposed |
change, website
|
| change.approved | Change approved for deployment |
change, website
|
| change.applied | Change deployed to site |
change, website
|
| change.pulled | Change pulled by integration |
change, website, integration
|
| change.verified | Pulled change verified after deploy |
change, website, integration
|
| change.rejected | Change rejected |
change, website, rejected_by, reason
|
| change.reverted | Applied change reverted |
change, website, reverted_by, revert_reason
|
Example Payload
Full payload for report.completed:
{
"event": "report.completed",
"delivery_id": "d4e5f6a7-b8c9-0123-4567-89abcdef0123",
"timestamp": "2024-02-01T08:05:00+00:00",
"data": {
"domain": "example.com",
"report_id": 7,
"type": "last_month",
"type_display": "Last Month",
"date": "2024-01-01",
"end_date": "2024-01-31",
"json_url": "/api/v2/websites/example.com/reports/7/",
"pdf_url": "/api/v2/websites/example.com/reports/7/pdf/"
}
}
Change Webhook Example
Full payload for change.approved:
{
"event": "change.approved",
"change": {
"id": 789,
"change_type": "internal_link",
"status": "approved",
"page_url": "https://example.com/blog/seo-tips",
"proposed_value": "<a href=\"/blog/link-building\">link building guide</a>",
"previous_value": null,
"reason": "High semantic similarity (0.92) between source and target pages",
"confidence_score": 0.95,
"anchor_text": "link building guide",
"created_at": "2024-02-08T14:00:00+00:00",
"reviewed_at": "2024-02-09T10:30:00+00:00",
"applied_at": null,
"pulled_at": null,
"pulled_by_integration": null,
"verified_at": null,
"reverted_at": null,
"revert_reason": null
},
"website": {"domain": "example.com"},
"timestamp": "2024-02-09T10:30:00+00:00"
}
Integration Examples
Python Client
A minimal client that wraps all common operations. Drop this into your project and call methods directly.
import time
import requests
class SEOJuiceClient:
def __init__(self, token: str, base_url: str = "https://seojuice.com/api/v2"):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.headers["Authorization"] = f"Bearer {token}"
def _request(self, method: str, path: str, **kwargs):
resp = self.session.request(method, f"{self.base_url}{path}", **kwargs)
resp.raise_for_status()
return resp.json()
def get_websites(self) -> list:
return self._request("GET", "/websites/")["results"]
def get_intelligence(self, domain: str, period: str = "30d") -> dict:
return self._request("GET", f"/websites/{domain}/intelligence/",
params={"period": period, "include_trends": "true"})
def get_content_gaps(self, domain: str, page: int = 1) -> dict:
return self._request("GET", f"/websites/{domain}/content-gaps/",
params={"page": page})
def get_aiso(self, domain: str, period: str = "30d") -> dict:
return self._request("GET", f"/websites/{domain}/aiso/",
params={"period": period, "include_history": "true"})
def get_competitors(self, domain: str) -> dict:
return self._request("GET", f"/websites/{domain}/competitors/",
params={"include_trends": "true"})
def analyze_page(self, domain: str, url: str, timeout: int = 120) -> dict:
"""Trigger analysis and poll until complete."""
result = self._request("POST", f"/websites/{domain}/analyze/",
json={"url": url})
status_url = result["status_url"]
deadline = time.time() + timeout
while time.time() < deadline:
status = self._request("GET", status_url)
if status["status"] == "completed":
return status
if status["status"] == "failed":
raise RuntimeError(f"Analysis failed: {status}")
time.sleep(5)
raise TimeoutError("Analysis did not complete in time")
# Usage
client = SEOJuiceClient("YOUR_API_TOKEN")
sites = client.get_websites()
intel = client.get_intelligence("example.com")
print(f"SEO Score: {intel['seo_score']}, AISO: {intel['aiso_score']}")
JavaScript Client
ES module client using the Fetch API. Works in Node.js 18+ and modern browsers.
class SEOJuiceClient {
constructor(token, baseUrl = "https://seojuice.com/api/v2") {
this.baseUrl = baseUrl.replace(/\/$/, "");
this.headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
};
}
async _request(method, path, opts = {}) {
const url = new URL(`${this.baseUrl}${path}`);
if (opts.params) {
Object.entries(opts.params).forEach(([k, v]) => url.searchParams.set(k, v));
}
const resp = await fetch(url, {
method,
headers: this.headers,
body: opts.body ? JSON.stringify(opts.body) : undefined,
});
if (!resp.ok) throw new Error(`API error ${resp.status}: ${await resp.text()}`);
return resp.json();
}
getWebsites() { return this._request("GET", "/websites/"); }
getIntelligence(domain, period = "30d") {
return this._request("GET", `/websites/${domain}/intelligence/`,
{ params: { period, include_trends: "true" } });
}
getContentGaps(domain, page = 1) {
return this._request("GET", `/websites/${domain}/content-gaps/`, { params: { page } });
}
getAISO(domain) {
return this._request("GET", `/websites/${domain}/aiso/`,
{ params: { period: "30d", include_history: "true" } });
}
async analyzePage(domain, url, timeoutMs = 120000) {
const result = await this._request("POST", `/websites/${domain}/analyze/`, { body: { url } });
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const status = await this._request("GET", result.status_url);
if (status.status === "completed") return status;
if (status.status === "failed") throw new Error("Analysis failed");
await new Promise(r => setTimeout(r, 5000));
}
throw new Error("Analysis timed out");
}
}
// Usage
const client = new SEOJuiceClient("YOUR_API_TOKEN");
const intel = await client.getIntelligence("example.com");
console.log(`SEO Score: ${intel.seo_score}, AISO: ${intel.aiso_score}`);
Webhook Receiver
Flask app that verifies HMAC signatures and routes events to handlers.
import hmac
import hashlib
import os
from flask import Flask, request, abort, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = os.environ["SEOJUICE_WEBHOOK_SECRET"]
def verify(payload: bytes, signature: str) -> bool:
expected = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route("/webhooks/seojuice", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-SEOJuice-Signature", "")
if not verify(request.get_data(), signature):
abort(401)
event = request.headers.get("X-SEOJuice-Event")
data = request.json["data"]
if event == "report.completed":
print(f"Report {data['report_id']} ready — PDF: {data['pdf_url']}")
elif event == "content_gap.found":
print(f"New gap: {data['page_name']} (potential: {data['seo_potential']})")
elif event == "aiso.score_changed":
print(f"AISO {data['old_score']} → {data['new_score']} ({data['change']:+d})")
elif event == "gbp.review_received":
print(f"New {data['rating']}★ review from {data['author']}")
return jsonify({"ok": True})
Claude Code / MCP
Use the SEOJuice API with AI coding assistants via the Model Context Protocol. Define tools so your AI assistant can query SEOJuice data directly.
{
"name": "seojuice_intelligence",
"description": "Get SEO intelligence summary for a website including SEO score, AISO score, content gaps, and trends.",
"input_schema": {
"type": "object",
"properties": {
"domain": {
"type": "string",
"description": "The website domain, e.g. example.com"
},
"period": {
"type": "string",
"enum": ["7d", "30d", "90d"],
"description": "Time period for metrics"
}
},
"required": ["domain"]
}
}
Example prompts for AI assistants:
- "Use the SEOJuice API to get intelligence for mysite.com and tell me what to prioritize."
- "Fetch content gaps for mysite.com and draft outlines for the top 5 by SEO potential."
- "Check my AISO score trends and suggest how to improve AI search visibility."
- "Analyze my competitor landscape and identify keywords where I'm close to overtaking them."