The GeoScored API is a REST API that returns JSON. Every request must include an API key header.
You can generate keys in Account Settings → API Keys.
Base URL
https://app.geoscored.ai/api/v1
Content type
application/json
The interactive OpenAPI specification is available at
/api/docs (Swagger UI) and
/api/redoc (ReDoc).
This page covers the concepts and patterns that the auto-generated docs do not.
2. Authentication
Every request requires an API key passed in the X-API-Key header.
Keys are scoped to your organization. All members of an org share the same scan history and credit balance.
# Pass your API key on every request
curl https://app.geoscored.ai/api/v1/scans \
-H "X-API-Key: geo_your_key_here" \
-H "Content-Type: application/json"
Key format
All keys begin with geo_. Requests with missing or malformed keys return 401.
Sandbox mode
Keys prefixed with geo_test_ activate sandbox mode. Sandbox scans do not consume credits and return synthetic data.
Org-scoped
API keys are scoped to your organization. Scans created by one key are visible to all keys in the same org.
3. Scan lifecycle
Scans are asynchronous. Creating a scan returns immediately with status pending.
The engine processes the scan in the background. Poll the scan endpoint or use a webhook callback to detect completion.
pendingCreated, queued
runningEngine active
completedfailedTerminal states
Polling pattern
Poll GET /api/v1/scans/{'{'}scan_id{'}'} until status is
COMPLETED or FAILED.
Most scans complete within 60–120 seconds. Use an exponential backoff starting at 5 seconds.
Webhook alternative
Pass a callback_url when creating a scan to receive a POST notification when the scan completes.
See the Webhooks section for payload details.
4. Endpoints
All endpoints require the X-API-Key header.
Method
Path
Description
POST
/api/v1/scans
Create a scan. Debits one credit.
GET
/api/v1/scans
List scans. Paginated, filterable.
GET
/api/v1/scans/compare
Compare two scans with computed deltas.
GET
/api/v1/scans/{'{scan_id}'}
Get a scan by ID (status + scores).
GET
/api/v1/scans/{'{scan_id}'}/report
Full audit report with all check results.
GET
/api/v1/scans/{'{scan_id}'}/checks
List all check results for a scan.
GET
/api/v1/scans/{'{scan_id}'}/checks/{'{check_id}'}
Get a single check result.
POST
/api/v1/scans/{'{scan_id}'}/share
Create a shareable report link.
PATCH
/api/v1/scans/{'{scan_id}'}
Update scan metadata (project, tracked URL).
DELETE
/api/v1/scans/{'{scan_id}'}
Delete a scan and all its records.
GET
/api/v1/scans/export
Export scan results as CSV.
GET
/api/v1/scans/{'{scan_id}'}/pdf
Download the PDF report for a completed scan.
POST/api/v1/scansRequires credit
Create a scan
Submits a URL for a GEO audit. The scan starts asynchronously. Returns 201 with the scan object in pending status.
One credit is debited from your organization's balance at creation time.
Request body
Field
Type
Required
Description
url
string
Yes
The URL to audit. Must be publicly accessible HTTPS.
brand_name
string
No
Brand name for AI mention checks. Defaults to the domain name.
callback_url
string
No
HTTPS URL to POST scan results to when complete. Max 2048 chars.
Returns the complete audit report for a completed scan: overall score, grade, pillar scores, all check results, and AI-generated summaries.
Only available once the scan status is COMPLETED.
Query parameters
Parameter
Type
Default
Description
audience
string
marketing
marketing or engineering. Switches explanatory text in why_it_matters, grade_descriptor, and pillar descriptions. Scores are identical for both.
{
"scan_id": "a1b2c3d4-...",
"url": "https://example.com",
"overall_score": 0.72,
"overall_grade": "B",
"grade_descriptor": "Good AI visibility with room to improve",
"pillar_scores": {
"geo": 0.68,
"seo": 0.81
},
"total_checks": 28,
"passing_checks": 19,
"warning_checks": 6,
"failing_checks": 3,
"checks": [/* array of check result objects */],
"geo_marketing_summary": "AI-generated summary...",
"access_level": "full",
"is_lite": false,
"audience": "marketing"
}
GET/api/v1/scans/compare
Compare two scans
Returns a structured comparison of two scans with computed deltas for overall score, each pillar, and each individual check.
Both scans must belong to your organization. Useful for tracking improvement over time.
Generates a 128-bit URL-safe share token. The token can be used to access the report at
/reports/share/{'{token}'} without authentication.
Tokens expire after 30 days and deactivate after 100 views. Maximum 10 tokens per scan per hour.
All errors return a structured JSON envelope with a consistent shape. Rather than inspecting HTTP status codes alone, always read the error.code field for machine-readable error classification.
{
"error": {
"type": "invalid_request",
"code": "invalid_url",
"message": "The URL must be a publicly accessible HTTPS address.",
"param": "url"
}
}
Common error codes
HTTP
code
Meaning
401
api_key_required
No X-API-Key header present.
401
api_key_invalid
Key does not exist, is revoked, or has wrong format.
402
insufficient_credits
No scan credits remaining. Purchase more to continue.
404
scan_not_found
Scan does not exist or belongs to a different org.
400
scan_not_complete
Report or checks requested before scan has finished.
422
invalid_url
URL is not a valid public HTTPS address.
422
invalid_callback_url
Callback URL must be HTTPS and publicly accessible.
429
org_rate_limit_exceeded
Organization-level rate limit reached. Back off and retry.
500
internal_error
Unexpected server error. Retrying usually resolves transient failures.
6. Rate limits
The API enforces two layers of rate limiting: IP-level limits applied before authentication, and organization-level limits applied after authentication.
Organization limits are configurable per org and may differ from the defaults shown here.
Limit group
Applies to
Headers
api_write
POST /api/v1/scans
X-RateLimit-*
api_read
All other GET endpoints
X-RateLimit-*
Rate limit information is returned in response headers:
429 responses include a Retry-After header with the number of seconds to wait before retrying.
Do not retry immediately on 429. Back off exponentially.
7. Webhooks
Pass a callback_url when creating a scan to receive a POST request when the scan completes.
The callback URL must be HTTPS and publicly reachable from the internet.
Callback payload
When a scan reaches a terminal state (COMPLETED or FAILED),
GeoScored sends a POST request to your callback URL with the scan object as the body.
Retry behavior: If your endpoint returns a non-2xx response, GeoScored retries up to 3 times with exponential backoff (5s, 25s, 125s). Your endpoint should respond within 10 seconds to avoid timeout.
8. Python examples
These examples use httpx, a modern HTTP client for Python. Install it with pip install httpx.