Documentation contents
Crawls
Crawl runs and their results, issues and pages.
List the crawls of a project
GET
/v1/projects/{project}/crawls
Crawl runs of a project, newest first.
- Required scopes
- crawls:read
- Rate-limit cost
- 1 unit(s)
Parameters
-
projectpath · string (uuid) · required -
cursorquery · string · optionalOpaque cursor from `meta.next_cursor`. It is bound to the request that issued it; reusing it with other filters returns `400 pagination.invalid_cursor`.
-
limitquery · integer · optionalItems per page, 1 to 100 (default 25).
Responses
-
200OK. -
400Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`). -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": [
{
"id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"campaign_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "queued",
"stage": "string",
"active": true,
"stage_plan": {
"current_step": 42,
"step_count": 42,
"settled": true,
"steps": [
{
"stage": "string",
"state": "pending",
"skip_reason": "string"
}
]
},
"pages": {
"crawled": 42,
"discovered": 42,
"budget": 42
},
"issue_counts": {
"error": 42,
"warning": 42,
"info": 42
},
"truncated": true,
"blocked": true,
"blocked_reason": "string",
"comparable": true,
"error": "string",
"trigger": "manual",
"started_at": "2026-10-01T09:30:00Z",
"finished_at": "2026-10-01T09:30:00Z",
"created_at": "2026-10-01T09:30:00Z"
}
],
"meta": {
"limit": 100,
"has_more": true,
"next_cursor": "string"
}
}
Get a crawl
GET
/v1/crawls/{crawl}
Status of a crawl run. Poll this resource while active is true.
status: completed is set before post-processing ends: wait for
stage_plan.settled to be true before reading reports.
- Required scopes
- crawls:read
- Rate-limit cost
- 1 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"campaign_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "queued",
"stage": "string",
"active": true,
"stage_plan": {
"current_step": 42,
"step_count": 42,
"settled": true,
"steps": [
{
"stage": "string",
"state": "pending",
"skip_reason": "string"
}
]
},
"pages": {
"crawled": 42,
"discovered": 42,
"budget": 42
},
"issue_counts": {
"error": 42,
"warning": 42,
"info": 42
},
"truncated": true,
"blocked": true,
"blocked_reason": "string",
"comparable": true,
"error": "string",
"trigger": "manual",
"started_at": "2026-10-01T09:30:00Z",
"finished_at": "2026-10-01T09:30:00Z",
"created_at": "2026-10-01T09:30:00Z"
}
}
List issue occurrences
GET
/v1/crawls/{crawl}/issues
One item per occurrence (an issue on a URL), in a stable order. Findings that describe a firewall or interstitial page rather than the site are excluded; the summary reports their number separately.
- Required scopes
- crawls:read
- Rate-limit cost
- 1 unit(s)
Parameters
-
crawlpath · string (uuid) · required -
cursorquery · string · optionalOpaque cursor from `meta.next_cursor`. It is bound to the request that issued it; reusing it with other filters returns `400 pagination.invalid_cursor`.
-
limitquery · integer · optionalItems per page, 1 to 100 (default 25).
-
severityquery · string · optional -
categoryquery · string · optional -
namequery · string · optionalExact issue name, as returned by the summary.
Responses
-
200OK. -
400Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`). -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": [
{
"name": "Exemple",
"severity": "error",
"category": "string",
"url": "https://exemple.fr/",
"details": "string"
}
],
"meta": {
"limit": 100,
"has_more": true,
"next_cursor": "string"
}
}
Summarise issues
GET
/v1/crawls/{crawl}/issues/summary
Issue families with their number of occurrences, and totals by severity.
- Required scopes
- crawls:read
- Rate-limit cost
- 5 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues/summary" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues/summary', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues/summary', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/issues/summary",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"totals": {
"error": 42,
"warning": 42,
"info": 42
},
"protection_occurrences": 42,
"groups": [
{
"name": "Exemple",
"severity": "error",
"category": "string",
"occurrences": 42
}
]
}
}
List crawled pages
GET
/v1/crawls/{crawl}/pages
Pages discovered by the crawl, in a stable order.
- Required scopes
- crawls:read
- Rate-limit cost
- 1 unit(s)
Parameters
-
crawlpath · string (uuid) · required -
cursorquery · string · optionalOpaque cursor from `meta.next_cursor`. It is bound to the request that issued it; reusing it with other filters returns `400 pagination.invalid_cursor`.
-
limitquery · integer · optionalItems per page, 1 to 100 (default 25).
-
statusquery · string · optionalHTTP status class; `unreachable` means no HTTP response at all.
-
internalquery · string · optional
Responses
-
200OK. -
400Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`). -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": [
{
"url": "https://exemple.fr/",
"status_code": 200,
"error_type": "string",
"content_type": "string",
"is_internal": true,
"depth": 42,
"title": "string",
"meta_description": "string",
"h1": "string",
"word_count": 42,
"canonical_url": "https://exemple.fr/",
"response_time_ms": 0.5,
"size_bytes": 42,
"internal_links": 42,
"external_links": 42,
"images": 42,
"images_without_alt": 42
}
],
"meta": {
"limit": 100,
"has_more": true,
"next_cursor": "string"
}
}
Get a page by URL
GET
/v1/crawls/{crawl}/pages/by-url
A crawled page and its issues, errors first.
- Required scopes
- crawls:read
- Rate-limit cost
- 1 unit(s)
Parameters
-
crawlpath · string (uuid) · required -
urlquery · string · requiredPage URL; matched on its normalised form.
Responses
-
200OK. -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages/by-url?url=https%3A%2F%2Fexemple.fr%2F" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages/by-url', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
'query' => [
'url' => 'https://exemple.fr/',
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages/by-url?url=https%3A%2F%2Fexemple.fr%2F', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pages/by-url?url=https%3A%2F%2Fexemple.fr%2F",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"url": "https://exemple.fr/",
"status_code": 200,
"error_type": "string",
"content_type": "string",
"is_internal": true,
"depth": 42,
"title": "string",
"meta_description": "string",
"h1": "string",
"word_count": 42,
"canonical_url": "https://exemple.fr/",
"response_time_ms": 0.5,
"size_bytes": 42,
"internal_links": 42,
"external_links": 42,
"images": 42,
"images_without_alt": 42,
"issues": [
{
"name": "Exemple",
"severity": "error",
"category": "string",
"url": "https://exemple.fr/",
"details": "string"
}
]
}
}
Read PageSpeed results
GET
/v1/crawls/{crawl}/pagespeed
Lighthouse performance of the PageSpeed sample (home page and category pages), on
mobile and desktop. A strategy that succeeded on no page is null, never 0; each
average travels with the number of pages it was measured on. combined is the mean
of the measured strategies, the one the health score uses. failed means every
measurement was refused.
- Required scopes
- crawls:read
- Rate-limit cost
- 1 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pagespeed" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pagespeed', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pagespeed', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/pagespeed",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "ready",
"average": {
"mobile": 42,
"desktop": 42,
"combined": 42,
"pages_measured_mobile": 42,
"pages_measured_desktop": 42,
"pages_sampled": 42
},
"results": [
{
"url": "https://exemple.fr/",
"mobile": {
"score": 42,
"metrics": {
"fcp": 0.5,
"lcp": 0.5,
"cls": 0.5,
"fid": 0.5,
"speed_index": 0.5,
"tti": 0.5
}
},
"desktop": {
"score": 42,
"metrics": {
"fcp": 0.5,
"lcp": 0.5,
"cls": 0.5,
"fid": 0.5,
"speed_index": 0.5,
"tti": 0.5
}
},
"analysis_date": "string",
"error": "string"
}
]
}
}
Read the GEO readiness report
GET
/v1/crawls/{crawl}/geo
AI search readiness, frozen at the end of the crawl: AI bot access (robots.txt),
llms.txt, schema.org inventory, editorial signals and the GEO score with its axes.
Detail sections follow the scoring version that produced them (score_version).
- Required scopes
- crawls:read
- Rate-limit cost
- 5 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/geo" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/geo', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/geo', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/geo",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "ready",
"score": 42,
"score_version": 42,
"score_axes": [
{
"key": "string",
"points": 0.5,
"max": 42,
"applicable": true
}
],
"origin": "string",
"access": {},
"llms_txt": {},
"structured_data": {},
"editorial": {},
"hints": [
{
"key": "string",
"priority": "high"
}
]
}
}
Read the accessibility report
GET
/v1/crawls/{crawl}/accessibility
Automated accessibility audit of the crawl (WCAG, RGAA rate), frozen at the end of
the crawl, with the pages it was measured on. Requires the campaign accessibility
option; otherwise unavailable. Manual checks list what no automated tool can
verify.
- Required scopes
- crawls:read
- Rate-limit cost
- 5 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/accessibility" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/accessibility', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/accessibility', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/accessibility",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "ready",
"score": 42,
"score_version": 42,
"standard": "string",
"origin": "string",
"rgaa_compliance_rate": 42,
"pages_analyzed": 42,
"pages_total": 42,
"rendered_pages_analyzed": 42,
"rendered_pages_total": 42,
"render_mode": "string",
"is_protected": true,
"contrast_status": "string",
"contrast_incomplete_count": 42,
"axes": [
{}
],
"conformance": {},
"rules": [
{}
],
"by_principle": [],
"contrast_pairs": [
{}
],
"hints": [
{
"key": "string",
"priority": "high"
}
],
"manual_checks": [
"string"
]
}
}
Read the trust foundation report
GET
/v1/crawls/{crawl}/eeat
E-E-A-T trust foundation: the score, the axes expected for the declared business
model and sector, and the attribution signals. An axis that does not apply is
non_applicable and leaves the denominator; it never counts as a failure.
previous_score is null when the previous record used another scoring version.
- Required scopes
- crawls:read
- Rate-limit cost
- 5 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/eeat" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/eeat', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/eeat', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/eeat",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "ready",
"score": 42,
"bareme_version": 42,
"business_model": "string",
"business_model_source": "string",
"declared_sector": "string",
"declared_fulfilment": [
"string"
],
"merchant_measured": true,
"ships_refuted": true,
"contradiction": "string",
"pages_analyzed": 42,
"truncated": true,
"axes": [
{
"key": "string",
"state": "string",
"points": 0.5,
"max": 42
}
],
"attribution": {},
"trust_pages": {},
"predicate_ranks": {},
"citations": {},
"previous_score": 42,
"comparable_to_previous": true
}
}
Read the internal linking report
GET
/v1/crawls/{crawl}/structure
Internal linking of the crawl: depth, inlinks, link scores (ranks, not grades),
orphan pages, anchors and leaks. Outside the health score. coverage says whether
JavaScript was rendered and whether edges were truncated: without it a low link
count reads as a linking defect when it is a measurement limit.
- Required scopes
- crawls:read
- Rate-limit cost
- 10 unit(s)
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/structure" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/structure', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/structure', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/structure",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "measured",
"error_reason": "string",
"pages_measured": 42,
"coverage": {},
"totals": {},
"leaks": {},
"anchors": {},
"boilerplate": {},
"pagerank": {},
"classification": {},
"depth": {},
"inlinks": {},
"score_depth": {},
"score_spread": {},
"top_authority": {},
"top_content": {},
"orphans": {},
"deepest": {},
"no_content_inlinks": {}
}
}
Compare with a baseline crawl
GET
/v1/crawls/{crawl}/compare
Differences with a baseline crawl of the same project: health, pages, issues by
severity, status codes, appeared and resolved issues and pages. Without baseline,
the previous comparable crawl is used; a truncated crawl is never a baseline
(crawl.no_baseline when none exists).
- Required scopes
- crawls:read
- Rate-limit cost
- 10 unit(s)
Parameters
-
crawlpath · string (uuid) · required -
baselinequery · string (uuid) · optionalThe baseline crawl identifier.
Responses
-
200OK. -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
429The plan rate limit is exhausted for one window.
cURL
curl "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/compare" \
-H "Authorization: Bearer $NESSFLOW_TOKEN"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('GET', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/compare', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/compare', {
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/compare",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"crawl_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"baseline_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"summary": {},
"status_codes": {},
"issues": {},
"pages": {}
}
}
Launch a crawl
POST
/v1/campaigns/{campaign}/crawls
Queues a crawl of the campaign and answers 202 with the crawl to poll
(Location). Consumes one crawl of the plan monthly quota, so an
Idempotency-Key is required: a retry after a timeout returns the first
response instead of launching a second crawl. One crawl runs at a time per
team (409 crawl.already_running, with the running crawl id).
- Required scopes
- crawls:write
- Rate-limit cost
- 1 unit(s)
- Idempotency-Key
- Idempotency-Key required
- Rate-limit cost
- Consumes a plan quota
Parameters
-
campaignpath · string (uuid) · required
Responses
-
202OK. -
400Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`). -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
409The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready). -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
429The plan rate limit is exhausted for one window.
cURL
curl -X POST "https://api.nessflow.com/v1/campaigns/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls" \
-H "Authorization: Bearer $NESSFLOW_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('POST', 'campaigns/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
'Idempotency-Key' => bin2hex(random_bytes(16)),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/campaigns/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
},
});
const data = await response.json();
Python
import os
import uuid
import requests
response = requests.post(
"https://api.nessflow.com/v1/campaigns/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/crawls",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"campaign_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "queued",
"stage": "string",
"active": true,
"stage_plan": {
"current_step": 42,
"step_count": 42,
"settled": true,
"steps": [
{
"stage": "string",
"state": "pending",
"skip_reason": "string"
}
]
},
"pages": {
"crawled": 42,
"discovered": 42,
"budget": 42
},
"issue_counts": {
"error": 42,
"warning": 42,
"info": 42
},
"truncated": true,
"blocked": true,
"blocked_reason": "string",
"comparable": true,
"error": "string",
"trigger": "manual",
"started_at": "2026-10-01T09:30:00Z",
"finished_at": "2026-10-01T09:30:00Z",
"created_at": "2026-10-01T09:30:00Z"
}
}
Stop a crawl
POST
/v1/crawls/{crawl}/stop
Stops a running crawl synchronously. Never refused for billing reasons.
- Required scopes
- crawls:write
- Rate-limit cost
- 1 unit(s)
- Idempotency-Key
- Idempotency-Key accepted
Parameters
-
crawlpath · string (uuid) · required
Responses
-
200OK. -
400Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`). -
401Missing, invalid or expired token, or a member who left the team. -
403The plan does not include the API (`api.not_in_plan`) or this verb (`api.write_not_in_plan`), or the token lacks a scope (`token.missing_scope`). -
404No such resource for the token's team. Resources of another team answer 404 as well, never 403. -
409The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready). -
422Invalid query or body values (`validation_failed`), with `errors` by field. -
429The plan rate limit is exhausted for one window.
cURL
curl -X POST "https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/stop" \
-H "Authorization: Bearer $NESSFLOW_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"
PHP
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.nessflow.com/v1/']);
$response = $client->request('POST', 'crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/stop', [
'headers' => [
'Authorization' => 'Bearer '.getenv('NESSFLOW_TOKEN'),
'Idempotency-Key' => bin2hex(random_bytes(16)),
],
]);
$data = json_decode((string) $response->getBody(), true);
JavaScript
const response = await fetch('https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/stop', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NESSFLOW_TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
},
});
const data = await response.json();
Python
import os
import uuid
import requests
response = requests.post(
"https://api.nessflow.com/v1/crawls/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/stop",
headers={
"Authorization": f"Bearer {os.environ['NESSFLOW_TOKEN']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=30,
)
response.raise_for_status()
data = response.json()
Response example
{
"data": {
"id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"campaign_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
"status": "queued",
"stage": "string",
"active": true,
"stage_plan": {
"current_step": 42,
"step_count": 42,
"settled": true,
"steps": [
{
"stage": "string",
"state": "pending",
"skip_reason": "string"
}
]
},
"pages": {
"crawled": 42,
"discovered": 42,
"budget": 42
},
"issue_counts": {
"error": 42,
"warning": 42,
"info": 42
},
"truncated": true,
"blocked": true,
"blocked_reason": "string",
"comparable": true,
"error": "string",
"trigger": "manual",
"started_at": "2026-10-01T09:30:00Z",
"finished_at": "2026-10-01T09:30:00Z",
"created_at": "2026-10-01T09:30:00Z"
}
}