API Documentation

Programmatic access to SEO intelligence, AI visibility scores, content analysis, and automated optimizations.

36 Endpoints REST API MCP Server Bearer Auth
Base URL: 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
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://seojuice.com/api/v2/websites/
Python
import requests

resp = requests.get(
    "https://seojuice.com/api/v2/websites/",
    headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
data = resp.json()
JavaScript
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:

Response — 401
{
  "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 object
{
  "pagination": {
    "page": 1,
    "page_size": 10,
    "total_count": 247,
    "total_pages": 25
  },
  "results": [...]
}

Error Handling

All errors return a consistent JSON structure:

Error response format
{
  "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

GET /api/v2/websites/

List all websites in your organization. Returns all websites in a single array (no pagination).

curl
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"}
  ]
}
GET /api/v2/websites/{domain}/

Get a single website with its latest report and key performance indicators.

Python
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
  }
}
GET /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
        }
      ]
    }
  ]
}
GET /api/v2/websites/{domain}/pages/{page_id}/

Get a single page with its internal links.

curl
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://seojuice.com/api/v2/websites/example.com/pages/4521/

Intelligence

GET /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
Python
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

GET /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

GET /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

GET /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
  }]
}
GET /api/v2/websites/{domain}/clusters/{cluster_id}/

Cluster detail with top keywords, time-series performance data, and quality history.

Content Gaps

GET /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

GET /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

GET /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

GET /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

POST /api/v2/websites/{domain}/analyze/

Trigger an asynchronous on-demand analysis for a page. Returns immediately with an analysis ID to poll.

curl
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
}
GET /api/v2/websites/{domain}/analyze/{analysis_id}/

Poll the status of an async page analysis. Status: queuedin_progresscompleted | 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

GET /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)
Python
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"
    }
  ]
}
GET /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
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"
      }
    }
  ]
}
GET /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
    }
  ]
}
GET /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
    }
  ]
}

Quality & SERP

GET /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
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"
}
GET /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)
Python
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
  }
}
GET /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
}
GET /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
Python
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"}
  ]
}
GET /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

GET /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
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

GET /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
Python
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
    }
  ]
}
GET /api/v2/websites/{domain}/changes/{change_id}/

Get a single change record by ID with full details including alternatives, optimization techniques, and LLM metadata.

Python
resp = requests.get(
    "https://seojuice.com/api/v2/websites/example.com/changes/789/",
    headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
change = resp.json()
POST /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.

Python
resp = requests.post(
    "https://seojuice.com/api/v2/websites/example.com/changes/789/approve/",
    headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
approved = resp.json()
POST /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.

Python
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()
POST /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.

Python
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()
POST /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.

Python
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()
POST /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.

Python
resp = requests.post(
    "https://seojuice.com/api/v2/websites/example.com/changes/789/verify/",
    headers={"Authorization": "Bearer YOUR_API_TOKEN"},
)
verified = resp.json()
POST /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
Python
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
}
GET /api/v2/websites/{domain}/changes/stats/

Get aggregated change statistics broken down by status and change type.

Python
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}
}
GET PATCH /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.

Python — update settings
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/*"
}
GET /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.

GET /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
  }]
}
POST /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
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"
}
GET /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/"
}
GET /api/v2/websites/{domain}/reports/{report_id}/pdf/

Download the report as a PDF file. Returns binary application/pdf.

curl
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -o report.pdf \
  https://seojuice.com/api/v2/websites/example.com/reports/7/pdf/

Google Business Profile

GET /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"
  }]
}
GET /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}
}
POST /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
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.

Claude Desktop — claude_desktop_config.json
{
  "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:

Python
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)
JavaScript
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:

JSON payload
{
  "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:

JSON payload
{
  "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.

Python
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.

JavaScript
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.

Python — Flask
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.

MCP tool definition
{
  "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."