Skip to content

Get the dead-link report

GET
/v1/sites/{id}/report
curl --request GET \
--url https://api.overwatch.weareheavy.dev/v1/sites/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/report \
--header 'x-api-key: <x-api-key>'

The dead-link report from the site’s most recent crawl run: the run summary plus every broken link found, ordered by target then source URL.

Rows are fanned out one per (target × linking page) for attribution; the headline counts (broken_count, internal_broken, external_broken) are distinct broken targets, not occurrences — one broken footer link counts once, not once per page. The site’s excludePaths globs are applied retroactively, so a report persisted before a settings change reflects the current crawl scope.

Responds 404 when the Broken Links app is not enabled for the site.

id
required
string format: uuid

The site’s UUID.

The latest run and its broken links.

Media typeapplication/json
object
run
required

A single crawl run and its summary metrics.

object
id
required
string format: uuid
tenant_id
required
string
site_id
required
string format: uuid
started_at
required

Epoch ms the run started.

integer
finished_at
required

Epoch ms the run finished, or null while running.

integer | null
state
required
string
Allowed values: running completed failed
pages_crawled
required
integer
links_checked
required
integer
broken_count
required

Distinct broken link targets found in the run.

integer
pages_with_broken_count
required

Distinct source pages that had at least one broken link.

integer
internal_broken
required
integer
external_broken
required
integer
a11y_score
required

Synthesized 0–100 accessibility score, or null when the run didn’t scan.

integer | null
<= 100
a11y_critical
required

Confirmed violations with critical impact.

integer
a11y_serious
required
integer
a11y_moderate
required
integer
a11y_minor
required
integer
a11y_pages_scanned
required

Internal pages the accessibility scan covered in this run.

integer
a11y_issue_count
required

Distinct confirmed (page, rule) accessibility violations this run (0 for crawl runs).

integer
a11y_resolved
required

Violations present in the previous accessibility run but gone in this one (the “fixed” trend).

integer
seo_score

Synthesized 0-100 SEO score, or null when the run didn’t extract SEO data.

integer | null
<= 100
seo_critical
integer
seo_serious
integer
seo_moderate
integer
seo_minor
integer
seo_pages_scanned

Pages an SEO snapshot was captured for (at most pages_crawled).

integer
seo_issue_count

Derived SEO issues at the time the run finalized.

integer
error
required

Failure reason if state is failed, otherwise null.

string | null
kind
required

crawl for a broken-link crawl, a11y for a standalone accessibility scan.

string
Allowed values: crawl a11y
brokenLinks
required
Array<object>

A single broken link found during a run.

object
id
required
string format: uuid
tenant_id
required
string
run_id
required
string format: uuid
source_url
required

The page that contained the link.

string format: uri
target_url
required

The link target that was found to be broken.

string format: uri
status
required

broken for a definitive failure status, error for an unreachable target.

string
Allowed values: broken error
status_code
required

HTTP status code of the target, or null if no response.

integer | null
kind
required
string
Allowed values: internal external
redirect_chain
required

The redirect chain that led to the failure, if any.

string | null
detail
required

A human-readable reason for the failure.

string | null
created_at
required

Epoch ms the broken link was recorded.

integer
link_text
required

Visible text of the linking anchor (best-effort).

string | null
link_selector
required

Best-effort positional CSS selector to the anchor in the source page.

string | null
link_attrs
required

JSON of the anchor’s id/class/rel/title/aria-label (best-effort).

string | null
Example
{
"run": {
"id": "1f5a0c9e-3b21-4a77-8c0e-2d4e6f8a1b22",
"tenant_id": "7c9e6f0a-1b2c-3d4e-5f60-718293a4b5c6",
"site_id": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"started_at": 1750000000000,
"finished_at": 1750000125000,
"state": "completed",
"pages_crawled": 482,
"links_checked": 1934,
"broken_count": 5,
"pages_with_broken_count": 4,
"internal_broken": 2,
"external_broken": 3,
"a11y_score": null,
"a11y_critical": 0,
"a11y_serious": 0,
"a11y_moderate": 0,
"a11y_minor": 0,
"a11y_pages_scanned": 0,
"a11y_issue_count": 0,
"a11y_resolved": 0,
"error": null,
"kind": "crawl"
},
"brokenLinks": [
{
"id": "c0ffee00-dead-beef-cafe-000000000001",
"tenant_id": "7c9e6f0a-1b2c-3d4e-5f60-718293a4b5c6",
"run_id": "1f5a0c9e-3b21-4a77-8c0e-2d4e6f8a1b22",
"source_url": "https://example.com/about",
"target_url": "https://example.com/team/old-page",
"status": "broken",
"status_code": 404,
"kind": "internal",
"redirect_chain": null,
"detail": "Not Found",
"created_at": 1750000125000,
"link_text": "Meet the team",
"link_selector": "main > section:nth-of-type(2) a:nth-of-type(1)",
"link_attrs": "{\"class\":\"team-link\"}"
}
]
}

Missing or invalid credentials.

Media typeapplication/json
object
error
required

A human-readable error message.

string
Example
{
"error": "invalid api key"
}

The site was not found, the Broken Links app is not enabled, or the site has no runs yet.

Media typeapplication/json
object
error
required

A human-readable error message.

string
Example
{
"error": "no runs yet"
}