Complete reference for the SEOgent REST API. All endpoints return JSON and use Bearer token authentication.
Base URL: https://seogent.ai/api
Every request must include your API token in the Authorization header:
Authorization: Bearer sk_your_api_key_here
Create tokens at Settings > API Tokens in the SEOgent dashboard. See the Getting Started guide for a walkthrough.
Requests without a valid token receive a 401 Unauthorized response.
All authenticated endpoints are rate-limited. The scan creation endpoint (POST /scans) has a stricter limit than read endpoints. Rate-limited requests receive a 429 Too Many Requests response with a Retry-After header.
POST /api/scans
Start a new SEO scan. Returns immediately with a scan_id — the scan runs asynchronously.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
domain |
string | Required without urls |
Domain to crawl (e.g. example.com or https://example.com) |
urls |
string[] | Required without domain |
Specific URLs to scan (max 1,000) |
crawl_mode |
string | No | discover (default for domain) or manual (default for urls) |
max_pages |
integer | Required for domain scans | Max pages to crawl (1–10,000) |
performance_scan |
boolean | No | Include Core Web Vitals (LCP, CLS, INP, etc.) |
accessibility_scan |
boolean | No | Run WCAG 2.1 AA accessibility audit |
link_check |
boolean | No | Check for dead links and broken images |
webhook_url |
string | No | URL to POST full results when scan completes |
You must provide either domain or urls (not both). When providing domain, max_pages is required.
Example — scan a domain:
curl -X POST https://seogent.ai/api/scans \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"max_pages": 100,
"performance_scan": true,
"link_check": true
}'
Example — scan specific URLs:
curl -X POST https://seogent.ai/api/scans \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/",
"https://example.com/about",
"https://example.com/pricing"
]
}'
Response 201 Created:
{
"scan_id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"status": "pending",
"domain": "example.com",
"crawl_mode": "Discover",
"total_urls": 0,
"estimated_credits": 500,
"webhook_url": null,
"message": "Scan queued successfully"
}
Validation errors 422:
{
"message": "Either a domain or a list of URLs is required.",
"errors": {
"domain": ["Either a domain or a list of URLs is required."]
}
}
Additional validation checks:
GET /api/scans/{scan_id}
Check the progress of a scan.
Example:
curl https://seogent.ai/api/scans/9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c \
-H "Authorization: Bearer sk_your_api_key_here"
Response 200 OK:
{
"scan_id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"domain": "example.com",
"url": "https://example.com",
"source": "api",
"status": "crawling",
"crawl_mode": "Discover",
"progress": {
"total_urls": 45,
"urls_crawled": 23,
"urls_analyzed": 15,
"percentage": 51
},
"timestamps": {
"created_at": "2026-03-12T12:00:00+00:00",
"started_at": "2026-03-12T12:00:02+00:00",
"crawling_completed_at": null,
"completed_at": null
}
}
Status values: pending → crawling → analyzing → completed | failed
GET /api/scans/{scan_id}/results
Retrieve full results for a completed scan. Results are cursor-paginated.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
issues_only |
boolean | false |
Only return pages that have failed checks |
min_severity |
string | — | Filter by score threshold: critical (<50), high (<70), medium (<90) |
per_page |
integer | 100 |
Results per page (max 200) |
cursor |
string | — | Cursor for the next page of results |
Example:
curl "https://seogent.ai/api/scans/9e1a2b3c.../results?issues_only=true&per_page=50" \
-H "Authorization: Bearer sk_your_api_key_here"
Response 200 OK:
{
"scan_id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"domain": "example.com",
"status": "completed",
"average_score": 74,
"summary": {
"excellent": 2,
"good": 12,
"needs_work": 15,
"poor": 3
},
"performance_summary": { "..." : "included if performance_scan was enabled" },
"accessibility_summary": { "..." : "included if accessibility_scan was enabled" },
"site_checks": {
"checks": [
{
"key": "robots_txt",
"name": "robots.txt",
"status": "passed",
"message": "robots.txt is accessible and valid",
"category": "crawlability"
}
],
"duplicate_titles": {
"count": 2,
"found": true,
"duplicates": [
{
"title": "Example Site",
"pages": [
"https://example.com/about",
"https://example.com/contact"
]
}
]
},
"duplicate_descriptions": {
"count": 0,
"found": false,
"duplicates": []
}
},
"top_issues": [
{ "issue": "Missing canonical tag", "count": 28 },
{ "issue": "Missing Open Graph tags", "count": 30 }
],
"results": {
"data": [
{
"url": "https://example.com/",
"score": 82,
"grade": "B",
"checks": 23,
"failed_checks": [
"No structured data found",
"Missing Open Graph tags"
],
"warnings": [
"Title could be longer (28 characters, recommend 30-60)"
],
"all_checks": [
{
"name": "Canonical Tag",
"key": "canonical",
"status": "failed",
"message": "No canonical tag found. Add a self-referencing canonical.",
"category": "indexability",
"weight": 8
}
],
"metadata": {
"title": "Example Site",
"description": "An example website",
"canonical": null
},
"performance": {
"score": 85,
"lcp": 1.8,
"fcp": 0.9,
"cls": 0.05,
"fid": 12,
"inp": 95,
"ttfb": 0.3,
"tbt": 120
},
"accessibility": {
"score": 72,
"violations": [
{
"id": "color-contrast",
"impact": "serious",
"help": "Elements must have sufficient color contrast",
"help_url": "https://dequeuniversity.com/rules/axe/4.4/color-contrast",
"wcag_tags": ["wcag2aa", "wcag143"],
"nodes": [
{
"target": [".hero-text"],
"html": "<p class=\"hero-text\">Welcome</p>",
"failure_summary": "Element has insufficient color contrast ratio of 3.2:1"
}
]
}
]
},
"analyzed_at": "2026-03-12T12:05:00+00:00"
}
],
"next_cursor": "eyJzY29yZSI6ODIsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0",
"prev_cursor": null,
"per_page": 100
}
}
The performance and accessibility fields are null for pages where those scans were not enabled or not yet completed.
Pagination: Use next_cursor to fetch the next page:
curl "https://seogent.ai/api/scans/9e1a2b3c.../results?cursor=eyJzY29yZSI6..." \
-H "Authorization: Bearer sk_your_api_key_here"
GET /api/scans
List your scans, newest first. Uses page-based pagination.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
Page number |
Example:
curl https://seogent.ai/api/scans \
-H "Authorization: Bearer sk_your_api_key_here"
Response 200 OK:
{
"data": [
{
"scan_id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"domain": "example.com",
"source": "api",
"status": "completed",
"crawl_mode": "Discover",
"progress": {
"total_urls": 32,
"urls_analyzed": 32,
"percentage": 100
},
"average_score": 74,
"created_at": "2026-03-12T12:00:00+00:00",
"completed_at": "2026-03-12T12:10:00+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 3,
"per_page": 20,
"total": 47
}
}
POST /api/scans/{scan_id}/cancel
Cancel a scan that is still in progress (pending, crawling, or analyzing).
Example:
curl -X POST https://seogent.ai/api/scans/9e1a2b3c.../cancel \
-H "Authorization: Bearer sk_your_api_key_here"
Response 200 OK:
{
"message": "Scan cancelled successfully."
}
Error — scan already finished 422:
{
"error": "This scan has already completed or failed.",
"error_code": "scan_already_terminal"
}
GET /api/domains
List all domains you have scanned.
Example:
curl https://seogent.ai/api/domains \
-H "Authorization: Bearer sk_your_api_key_here"
Response 200 OK:
{
"data": [
{
"id": 1,
"domain": "example.com",
"total_scans": 5,
"last_scanned_at": "2026-03-12T12:10:00+00:00"
}
]
}
GET /api/credits
Check your remaining credits and auto-renew status.
Example:
curl https://seogent.ai/api/credits \
-H "Authorization: Bearer sk_your_api_key_here"
Response 200 OK:
{
"balance": 1500,
"auto_renew": true
}
Found in results.data[].all_checks[] and site_checks.checks[].
| Field | Type | Description |
|---|---|---|
name |
string | Human-readable check name |
key |
string | Machine identifier (e.g. canonical, meta_title) |
status |
string | passed, failed, or warning |
message |
string | Description of the finding |
category |
string | meta, indexability, crawlability, performance, content |
weight |
number | Scoring impact (page-level checks only) |
| Grade | Score Range |
|---|---|
| A | 90–100 (excellent) |
| B | 70–89 (good) |
| C | 50–69 (needs work) |
| D / F | 0–49 (poor) |
All errors return a JSON object with an errors field for validation failures.
| Status | Error Code | Description |
|---|---|---|
401 |
— | Missing or invalid API token |
404 |
scan_not_found |
Scan does not exist or does not belong to you |
422 |
— | Validation error (see errors object for details) |
422 |
scan_already_terminal |
Attempted to cancel a completed or failed scan |
429 |
— | Rate limited — check Retry-After header |
When you provide a webhook_url on scan creation, SEOgent sends a POST request to that URL when the scan completes. The payload is the same JSON structure as the Get Scan Results endpoint.
# 1. Start a scan
SCAN_ID=$(curl -s -X POST https://seogent.ai/api/scans \
-H "Authorization: Bearer $SEOGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"example.com","max_pages":100}' | jq -r '.scan_id')
# 2. Poll status
curl -s https://seogent.ai/api/scans/$SCAN_ID \
-H "Authorization: Bearer $SEOGENT_API_KEY" | jq '.status'
# 3. Fetch results when completed
curl -s https://seogent.ai/api/scans/$SCAN_ID/results \
-H "Authorization: Bearer $SEOGENT_API_KEY" | jq '.top_issues'
SCAN_ID=$(curl -s -X POST https://seogent.ai/api/scans \
-H "Authorization: Bearer $SEOGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"example.com","max_pages":50}' | jq -r '.scan_id')
while true; do
sleep 15
STATUS=$(curl -s https://seogent.ai/api/scans/$SCAN_ID \
-H "Authorization: Bearer $SEOGENT_API_KEY" | jq -r '.status')
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && echo "Scan failed" && exit 1
done
SCORE=$(curl -s https://seogent.ai/api/scans/$SCAN_ID/results \
-H "Authorization: Bearer $SEOGENT_API_KEY" | jq '.average_score')
[ "$SCORE" -lt 70 ] && echo "SEO score $SCORE is below threshold" && exit 1
curl -X POST https://seogent.ai/api/scans \
-H "Authorization: Bearer $SEOGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"max_pages": 100,
"webhook_url": "https://your-server.com/seo-callback"
}'