curl -X POST https://api.pocketutils.com/v1/broken-links \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
const res = await fetch("https://api.pocketutils.com/v1/broken-links", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com" }),
});
const { data } = await res.json();
{
"success": true,
"data": {
"url": "https://example.com",
"scannedAt": "2026-07-21T09:14:02.000Z",
"totalLinks": 42,
"checkedLinks": 42,
"truncated": false,
"healthScore": 88,
"grade": "B+",
"summary": { "ok": 37, "warnings": 2, "broken": 3, "info": 0, "internal": 30, "external": 12 },
"statusBreakdown": [
{ "code": "200", "label": "OK", "severity": "ok", "count": 37 },
{ "code": "404", "label": "Not Found", "severity": "broken", "count": 3 }
],
"links": [
{
"url": "https://example.com/old-page",
"originalHref": "/old-page",
"text": "Learn more",
"type": "internal",
"rel": null,
"statusCode": 404,
"statusText": "Not Found",
"severity": "broken",
"reason": "The page no longer exists at this URL.",
"category": "Client Error (4xx)",
"redirected": false,
"responseTimeMs": 182
}
]
}
}
Crawling & Link Extraction
Broken Link Checker
Scan a single page, or crawl an entire site, for dead links with exact HTTP status codes and reasons.
POST
/
v1
/
broken-links
curl -X POST https://api.pocketutils.com/v1/broken-links \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
const res = await fetch("https://api.pocketutils.com/v1/broken-links", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com" }),
});
const { data } = await res.json();
{
"success": true,
"data": {
"url": "https://example.com",
"scannedAt": "2026-07-21T09:14:02.000Z",
"totalLinks": 42,
"checkedLinks": 42,
"truncated": false,
"healthScore": 88,
"grade": "B+",
"summary": { "ok": 37, "warnings": 2, "broken": 3, "info": 0, "internal": 30, "external": 12 },
"statusBreakdown": [
{ "code": "200", "label": "OK", "severity": "ok", "count": 37 },
{ "code": "404", "label": "Not Found", "severity": "broken", "count": 3 }
],
"links": [
{
"url": "https://example.com/old-page",
"originalHref": "/old-page",
"text": "Learn more",
"type": "internal",
"rel": null,
"statusCode": 404,
"statusText": "Not Found",
"severity": "broken",
"reason": "The page no longer exists at this URL.",
"category": "Client Error (4xx)",
"redirected": false,
"responseTimeMs": 182
}
]
}
}
Three endpoints, sharing one link-checking engine: scan one page synchronously, or start a site-wide crawl (via
See the full error reference for the platform-level codes (
sitemap.xml and internal links) and poll it to completion. Every discovered link is resolved, checked over the network, and classified by severity.
2 credits per call (single-page scan or crawl start)
Scan a single page
POST /v1/broken-links checks every link found on one page and returns immediately.
Body parameters
string
required
The page to scan.
https:// is assumed if the scheme is omitted.Response
object
required
Hide properties
Hide properties
string
required
Echoes the scanned URL.
string
required
ISO 8601 timestamp.
integer
required
Total links discovered on the page.
integer
required
How many were actually checked (capped for performance).
boolean
required
true if the page had more links than the scan cap.integer
required
0–100.
string
required
Letter grade derived from
healthScore.object
required
Show properties
Show properties
integer
required
Links that returned a healthy status.
integer
required
Links with a non-fatal issue (e.g. a redirect chain).
integer
required
Links that are dead.
integer
required
Links that were noted but not scored (e.g. a non-HTTP scheme).
integer
required
Links to the same site.
integer
required
Links to a different site.
array
required
array
required
Show item shape
Show item shape
string
required
Resolved absolute URL.
string
required
href exactly as authored.
string
required
Anchor text.
string
required
"internal", "external", "mailto", "tel", "anchor", or "other".string
The
rel attribute, if any.integer | null
required
HTTP status, or
null when there was no response.string
required
HTTP status text, or the network error label.
string
required
"ok", "warning", "broken", or "info".string
required
URL-specific explanation of the outcome.
string
required
Grouping label, e.g.
"Client Error (4xx)", "Network Error".boolean
required
true if the link responded with a 3xx redirect.string
Redirect target, present when
redirected is true.integer
Round-trip time, present when a response was received.
string
Low-level network error, e.g.
"ENOTFOUND", when applicable.curl -X POST https://api.pocketutils.com/v1/broken-links \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
const res = await fetch("https://api.pocketutils.com/v1/broken-links", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com" }),
});
const { data } = await res.json();
{
"success": true,
"data": {
"url": "https://example.com",
"scannedAt": "2026-07-21T09:14:02.000Z",
"totalLinks": 42,
"checkedLinks": 42,
"truncated": false,
"healthScore": 88,
"grade": "B+",
"summary": { "ok": 37, "warnings": 2, "broken": 3, "info": 0, "internal": 30, "external": 12 },
"statusBreakdown": [
{ "code": "200", "label": "OK", "severity": "ok", "count": 37 },
{ "code": "404", "label": "Not Found", "severity": "broken", "count": 3 }
],
"links": [
{
"url": "https://example.com/old-page",
"originalHref": "/old-page",
"text": "Learn more",
"type": "internal",
"rel": null,
"statusCode": 404,
"statusText": "Not Found",
"severity": "broken",
"reason": "The page no longer exists at this URL.",
"category": "Client Error (4xx)",
"redirected": false,
"responseTimeMs": 182
}
]
}
}
Crawl an entire site
For more than one page, start an async crawl and poll it — see Async Jobs & Polling for the full pattern, phase list, and crawl limits (250 pages, 6-minute budget).Start the crawl
POST /v1/broken-links/crawl, body { "url": "..." } — same as the single-page scan, any URL on the site.
Returns 202 immediately:
{ "success": true, "data": { "jobId": "c3d4e5f6-1234-4a5b-8c9d-0e1f2a3b4c5d" } }
Poll the status
GET /v1/broken-links/crawl/:jobId — free and uncached. Returns the standard job envelope. Once status is "done", result holds a SiteCrawlResult: the same summary/statusBreakdown/grade/healthScore shape as the single-page result, plus:
integer
required
Internal pages fetched.
integer
required
Internal pages known (crawled + queued).
integer
required
Unique links found across every crawled page.
boolean
required
true if the 250-page cap was hit.boolean
required
true if the 1,500-link check cap was hit.array
required
Same shape as the single-page
links, plus sources (sample source pages, capped at 25) and sourceCount (total pages the link appeared on).array
required
Every page crawled:
url, statusCode, statusText, severity, linksOnPage, depth (BFS depth from the entry URL), fromSitemap.curl -X POST https://api.pocketutils.com/v1/broken-links/crawl \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
curl https://api.pocketutils.com/v1/broken-links/crawl/c3d4e5f6-1234-4a5b-8c9d-0e1f2a3b4c5d \
-H "Authorization: Bearer YOUR_API_KEY"
{
"success": true,
"data": {
"jobId": "c3d4e5f6-1234-4a5b-8c9d-0e1f2a3b4c5d",
"status": "crawling",
"progress": {
"phase": "crawling",
"message": "Crawling internal pages…",
"sitemapFound": true,
"sitemapUrlCount": 58,
"pagesCrawled": 22,
"pagesDiscovered": 58,
"pagesCap": 250,
"linksFound": 340,
"linksChecked": 0,
"linksTotal": 0
}
}
}
Errors
| Status | Cause |
|---|---|
400 | url missing from the request body |
404 | jobId doesn’t exist or its 30-minute window has expired |
401, 402, 429) that apply to every endpoint.Was this page helpful?