DOCS
Open the app ↗

API reference

The REST API for reading your graph, score and recommendations — and for starting scans. JSON responses, authenticated with API keys.

Base URL

All endpoints live under the /v1 prefix and are served over HTTPS only.

Base URL
https://api.graphorank.com

Authentication

Generate a key from the app's API & MCP page. Keys are prefixedgr_, shown in full only once at creation, and stored hashed. Send the key in the X-Api-Key header on every request.

Keys carry scopes: read (always granted) covers every GET endpoint, while write is required to create sites, start scans and manage schedules. The app's own session uses a JWT in the Authorization: Bearer header instead — API keys are for programmatic access.

Shell
# Create a key in the app under API & MCP — keys start with "gr_"
export GRAPHORANK_API_KEY="gr_..."

curl https://api.graphorank.com/v1/sites \
  -H "X-Api-Key: $GRAPHORANK_API_KEY"

Endpoints

GET/v1/sitesList the sites in your workspace.
POST/v1/sitesAdd a site.
GET/v1/sites/{site_id}Fetch a single site.
POST/v1/sites/{site_id}/verifyRun the ownership check with the method you picked.
POST/v1/sites/{site_id}/scansStart an on-demand scan.
GET/v1/scans/{scan_id}Poll a scan until status is done, then read its graph, score and recommendations.
GET/v1/scans/{scan_id}/graphNodes carry the role (pillar | cluster | hub | money | orphan | noindex), structural and semantic cluster ids, PageRank, in/out degree, click depth and indexability.
GET/v1/scans/{scan_id}/scoreGraphRank and the four area scores for a completed scan.
GET/v1/scans/{scan_id}/recommendationsList the scan's recommendations, most severe first.
GET/v1/sites/{site_id}/historyGraphRank time series for the site, one point per completed scan, ordered by timestamp.
GET/v1/sites/{site_id}/diffCompare two scans of the same site.
POST/v1/sites/{site_id}/scheduleCreate or update the site's schedule (upsert — one per site).
GET/v1/sites/{site_id}/scheduleRead the current schedule.
DELETE/v1/sites/{site_id}/scheduleRemove the schedule.
GET/v1/usagePlan, billing status and quota consumption for the workspace.
GET/v1/api-keysList the workspace's keys.
POST/v1/api-keysCreate a key.
DELETE/v1/api-keys/{key_id}Revoke a key.

Sites

A site is a root domain in your workspace. Add it, prove you own it, then scan it. Ownership uses a well-known file or a meta tag — the exact token snippet lives in Getting started.

GET/v1/sites

List the sites in your workspace.

200 · JSON
[
  {
    "id": "0d9f1c4e-6a3b-4f6e-9a2d-1b7c8e5f0a21",
    "root_url": "https://rovere-arredamenti.com",
    "label": "Rovere Arredamenti",
    "verified": true,
    "created_at": "2026-06-02T08:15:00Z"
  }
]

POST/v1/sites

Add a site. Requires the write scope. Returns 201 with the new site — verified starts as false.

root_urlbody · string (uri)requiredRoot URL of the site, e.g. https://yourdomain.com.
labelbody · stringOptional display name, up to 200 characters.
Shell
curl -X POST https://api.graphorank.com/v1/sites \
  -H "X-Api-Key: $GRAPHORANK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"root_url": "https://rovere-arredamenti.com", "label": "Rovere Arredamenti"}'
201 · JSON
{
  "id": "0d9f1c4e-6a3b-4f6e-9a2d-1b7c8e5f0a21",
  "root_url": "https://rovere-arredamenti.com",
  "label": "Rovere Arredamenti",
  "verified": false,
  "created_at": "2026-07-03T09:12:44Z"
}

GET/v1/sites/{site_id}

Fetch a single site. The response has the same shape as a list item of GET /v1/sites.

site_idpath · uuidrequiredSite identifier.

POST/v1/sites/{site_id}/verify

Run the ownership check with the method you picked. The response always includes your site token, so you can call this endpoint once to retrieve it before publishing the file or meta tag.

site_idpath · uuidrequiredSite identifier.
methodbody · stringrequiredVerification method: "file" (well-known file) or "meta" (homepage meta tag).
Shell
curl -X POST https://api.graphorank.com/v1/sites/{site_id}/verify \
  -H "X-Api-Key: $GRAPHORANK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"method": "file"}'
200 · JSON
{
  "verified": true,
  "method": "file",
  "token": "9Kd2mQx7Ln4pTv8waZrGQg",
  "detail": "token found at /.well-known/graphorank-verify.txt"
}

Scans

A scan runs the full pipeline: crawl, semantic analysis, graph build, scoring. Its status moves through queued → crawling → embedding → scoring → done (or failed, with an error code). Scans count toward your plan's monthly quota.

POST/v1/sites/{site_id}/scans

Start an on-demand scan. Requires the write scope. Returns 202 with the queued scan; when the monthly quota is exhausted you get 402 quota_exceeded.

site_idpath · uuidrequiredSite identifier.
render_modebody · stringOptional: "http", "js" or "auto". Omit to use the default — auto enables JS rendering only when the site needs it.
Shell
curl -X POST https://api.graphorank.com/v1/sites/{site_id}/scans \
  -H "X-Api-Key: $GRAPHORANK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"render_mode": "auto"}'
202 · JSON
{
  "id": "7f2a4c0e-9d1b-4b8a-b6c3-2e5f8a7d9c10",
  "site_id": "0d9f1c4e-6a3b-4f6e-9a2d-1b7c8e5f0a21",
  "status": "queued",
  "render_mode": "auto",
  "pages_count": 0,
  "error_code": null,
  "error_detail": null,
  "started_at": null,
  "finished_at": null,
  "created_at": "2026-07-03T09:38:12Z"
}

GET/v1/scans/{scan_id}

Poll a scan until status is done, then read its graph, score and recommendations.

scan_idpath · uuidrequiredScan identifier.
200 · JSON
{
  "id": "7f2a4c0e-9d1b-4b8a-b6c3-2e5f8a7d9c10",
  "site_id": "0d9f1c4e-6a3b-4f6e-9a2d-1b7c8e5f0a21",
  "status": "done",
  "render_mode": "auto",
  "pages_count": 214,
  "error_code": null,
  "error_detail": null,
  "started_at": "2026-07-03T09:38:20Z",
  "finished_at": "2026-07-03T09:40:41Z",
  "created_at": "2026-07-03T09:38:12Z"
}

Graph

The complete node/edge graph of a completed scan, in the same format the app's interactive view consumes. Large graphs are served in cursor pages of 1,000 elements (nodes first, then edges): keep requesting with the returned next_cursor until it comes back null. The meta object is repeated on every page.

GET/v1/scans/{scan_id}/graph

Nodes carry the role (pillar | cluster | hub | money | orphan | noindex), structural and semantic cluster ids, PageRank, in/out degree, click depth and indexability. Edges are internal links.

scan_idpath · uuidrequiredScan identifier.
cursorquery · integerCursor returned by the previous page. Defaults to 0 (first page).
Shell
curl "https://api.graphorank.com/v1/scans/{scan_id}/graph?cursor=0" \
  -H "X-Api-Key: $GRAPHORANK_API_KEY"
200 · JSON
{
  "nodes": [
    {
      "id": "d41f4d6a-1a52-4b0e-8c7a-b9e4f1c2a3d4",
      "url": "https://rovere-arredamenti.com/collezioni/tavoli",
      "role": "pillar",
      "cluster_struct": 3,
      "cluster_sem": 2,
      "pagerank": 0.0184,
      "in_degree": 42,
      "out_degree": 18,
      "depth": 1,
      "indexable": true
    }
  ],
  "edges": [
    { "s": "d41f4d6a-…", "t": "9c2b1e07-…", "type": "internal" }
  ],
  "meta": {
    "nodes": 214,
    "edges": 1310,
    "modularity": 0.62,
    "avg_depth": 2.4,
    "orphan_count": 7,
    "cluster_agreement": 0.81
  },
  "next_cursor": 1000
}

Score

GraphRank is a 0–100 benchmark of how visible a site is to generative engines. The endpoint returns it together with the four area scores shown in the app — structural, topical, GEO and technical — plus the detail object that powers the in-app explanations. The methodology behind the number is proprietary and applied identically to every site.

GET/v1/scans/{scan_id}/score

GraphRank and the four area scores for a completed scan.

scan_idpath · uuidrequiredScan identifier.
200 · JSON
{
  "scan_id": "7f2a4c0e-9d1b-4b8a-b6c3-2e5f8a7d9c10",
  "graphrank": 68.4,
  "sub_structural": 78.2,
  "sub_topical": 71.5,
  "sub_geo": 54.1,
  "sub_tech": 49.8,
  "detail": { … }
}

Recommendations

Prioritized fixes generated from the scan, each with a severity (P0 blocking, P1 important, P2 nice-to-have), localized messages and a meta object with the affected URL plus extra context.

GET/v1/scans/{scan_id}/recommendations

List the scan's recommendations, most severe first.

scan_idpath · uuidrequiredScan identifier.
severityquery · stringOptional filter: "P0", "P1" or "P2".
Shell
curl "https://api.graphorank.com/v1/scans/{scan_id}/recommendations?severity=P0" \
  -H "X-Api-Key: $GRAPHORANK_API_KEY"
200 · JSON
[
  {
    "id": "b7e3a9c1-5d2f-4e8b-a1c6-3f9d7e2b8a54",
    "page_id": "9c2b1e07-4f6a-4d3e-b8a1-7c5e9f2d1b36",
    "type": "orphan_page",
    "severity": "P0",
    "message_it": "Pagina orfana: nessun link interno la raggiunge. Collegala dalla sezione pertinente.",
    "message_en": "Orphan page: no internal link reaches it and crawlers struggle to find it. Link it from the relevant section.",
    "meta": { "url": "https://rovere-arredamenti.com/blog/cura-del-rovere" }
  }
]

History & diff

Track how the score evolves across scans, and diff two of them to see exactly what changed — pages added and removed, orphans, indexability.

GET/v1/sites/{site_id}/history

GraphRank time series for the site, one point per completed scan, ordered by timestamp.

site_idpath · uuidrequiredSite identifier.
fromquery · date-timeOnly points at or after this ISO 8601 timestamp.
toquery · date-timeOnly points at or before this ISO 8601 timestamp.
200 · JSON
[
  {
    "ts": "2026-06-05T03:00:00Z",
    "graphrank": 64.2,
    "sub_structural": 76.0,
    "sub_topical": 70.1,
    "sub_geo": 50.3,
    "sub_tech": 47.9
  },
  {
    "ts": "2026-07-03T09:40:41Z",
    "graphrank": 68.4,
    "sub_structural": 78.2,
    "sub_topical": 71.5,
    "sub_geo": 54.1,
    "sub_tech": 49.8
  }
]

GET/v1/sites/{site_id}/diff

Compare two scans of the same site. Without parameters it compares the two most recent completed scans. score_delta and sub_deltas are null when either scan has no score.

site_idpath · uuidrequiredSite identifier.
from_scanquery · uuidBaseline scan. Defaults to the second most recent completed scan.
to_scanquery · uuidTarget scan. Defaults to the most recent completed scan.
200 · JSON
{
  "from_scan": "5b1c8e2d-7a4f-4c9b-8d3e-1f6a2c7b9e05",
  "to_scan": "7f2a4c0e-9d1b-4b8a-b6c3-2e5f8a7d9c10",
  "score_delta": 4.2,
  "sub_deltas": { "structural": 2.2, "topical": 1.4, "geo": 3.8, "tech": 1.9 },
  "pages_added": {
    "count": 6,
    "sample": ["https://rovere-arredamenti.com/blog/cura-del-rovere"]
  },
  "pages_removed": {
    "count": 1,
    "sample": ["https://rovere-arredamenti.com/vecchia-landing"]
  },
  "orphans_from": 12,
  "orphans_to": 7,
  "indexable_from": 195,
  "indexable_to": 203
}

Scheduled scans

One recurring scan per site, with an email report on completion. Available on Pro and Agency plans — on lower tiers these endpoints return 402 quota_exceeded with an upgrade hint.

POST/v1/sites/{site_id}/schedule

Create or update the site's schedule (upsert — one per site). Requires the write scope.

site_idpath · uuidrequiredSite identifier.
freqbody · stringrequired"weekly" or "monthly".
daybody · integerrequiredWeekly: 0–6 with Monday = 0. Monthly: 1–28.
timebody · stringrequired24-hour "HH:MM", UTC.
recipientsbody · string[]requiredUp to 10 email addresses that receive the report.
activebody · booleanDefaults to true.
Shell
curl -X POST https://api.graphorank.com/v1/sites/{site_id}/schedule \
  -H "X-Api-Key: $GRAPHORANK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"freq": "weekly", "day": 0, "time": "03:00", "recipients": ["seo@rovere-arredamenti.com"]}'
200 · JSON
{
  "id": "3e8d1f5a-2b7c-4a9e-b4d1-6c8f2a5e7b93",
  "site_id": "0d9f1c4e-6a3b-4f6e-9a2d-1b7c8e5f0a21",
  "freq": "weekly",
  "day": 0,
  "time": "03:00",
  "recipients": ["seo@rovere-arredamenti.com"],
  "active": true,
  "last_run_at": null,
  "created_at": "2026-07-03T10:02:19Z"
}

GET/v1/sites/{site_id}/schedule

Read the current schedule. Returns 404 not_found when none is configured.

site_idpath · uuidrequiredSite identifier.

DELETE/v1/sites/{site_id}/schedule

Remove the schedule. Requires the write scope. Returns 204 No Content.

site_idpath · uuidrequiredSite identifier.

Usage

Where you stand against your plan's quota in the current billing period. Limits come back null when unlimited.

GET/v1/usage

Plan, billing status and quota consumption for the workspace.

200 · JSON
{
  "tier": "pro",
  "billing_status": "active",
  "scans_used": 4,
  "scans_limit": 6,
  "pages_limit": 500,
  "sites_used": 2,
  "sites_limit": 3
}

API keys

Workspace-level keys for programmatic access, prefixed gr_ and stored hashed. The plaintext key is returned only once, in the creation response — store it in a secret manager. Scopes: read (always included) covers every GET endpoint; write is required for anything that mutates state.

GET/v1/api-keys

List the workspace's keys. Secrets are never returned after creation.

200 · JSON
[
  {
    "id": "a1f7c3e9-8b2d-4e6a-9c5f-2d7b4a8e1c60",
    "scopes": ["read"],
    "created_at": "2026-06-20T11:05:00Z",
    "last_used_at": "2026-07-03T09:41:07Z"
  }
]

POST/v1/api-keys

Create a key. The key field in the response is the only time the secret is visible.

scopesbody · string[]"read" and/or "write". "read" is always granted; unknown scopes return 422.
Shell
curl -X POST https://api.graphorank.com/v1/api-keys \
  -H "X-Api-Key: $GRAPHORANK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scopes": ["read", "write"]}'
201 · JSON
{
  "id": "e4b8d2f6-1c9a-4d7e-8b3c-5f2a7d9c1e48",
  "key": "gr_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "scopes": ["read", "write"],
  "created_at": "2026-07-03T10:15:33Z"
}

DELETE/v1/api-keys/{key_id}

Revoke a key. It stops working immediately. Returns 204 No Content.

key_idpath · uuidrequiredKey identifier.

Rate limits & errors

Two token buckets protect the API: one per client IP (applied before authentication) and one per workspace (applied after). When you hit a limit the response is 429 with a Retry-After header telling you how many seconds to wait.

Errors use the application/problem+json format — { type, title, status, detail, code }. Request-validation errors (422) return the standard detail array pointing at the offending fields.

402 · problem+json
{
  "type": "about:blank",
  "title": "Quota Exceeded",
  "status": 402,
  "detail": "monthly scan quota reached (6/6): upgrade your plan or wait for the next period",
  "code": "quota_exceeded"
}
401unauthorizedMissing or invalid credentials (X-Api-Key or Bearer JWT).
402quota_exceededPlan quota reached, or the feature needs a higher tier. Upgrade or wait for the next period.
403forbiddenThe key's scopes don't allow this call (e.g. write is required).
404not_foundThe resource doesn't exist in your workspace — you never see other tenants' data.
422validation_errorMalformed body or parameters; the response lists the offending fields.
429rate_limitedToo many requests — honor the Retry-After header (seconds).
503service_unavailableA downstream dependency is unavailable. Retry later.