Skip to content
Developers

Crawls reference

Sites and crawls: read them, their links, AI readiness and exports.

List crawls

GET/crawls

Scope:
read
Rate class:
light
Operation id:
list_crawls

Your crawls, newest first. These are the crawls you started, scheduled or that started a new site (the dashboard's Scans list); the product's own "Check if fixed" runs are not crawls. Filter by site_id (the dashboard's per-site list: a root site also lists its sub-folder crawls), status, mode and since (crawls queued at or after this time, ISO 8601, UTC when it has no zone; write the zone as Z or %2B02:00). Paged with cursor. Each crawl carries its live progress and processing, so one call is enough to follow several crawls. Free; needs read.

Parameters

NameInTypeDescription
site_idqueryintegeroptionalOnly crawls of this site (an id from GET /sites).Range: 1 to 2147483647.
statusquerystringoptionalOnly crawls in this status.One of: pending, running, completed, failed, cancelled, partial.
modequerystringoptionalOnly fast or only full (detailed) crawls.One of: fast, full.
sincequerystringoptionalOnly crawls queued at or after this time (ISO 8601; UTC if no zone).
limitqueryintegeroptionalItems per page, 1 to 200.Range: 1 to 200. Default: 50.
cursorquerystringoptionalThe next_cursor of the previous page. Opaque: never build one.

Answer 200

OK. The body is a CrawlPage.

FieldTypeDescription
dataarray of objectThe items of this page, in the endpoint's fixed order.
data[].idstringThe crawl's id (a UUID).
data[].site_idinteger or nullThe most specific site of yours this crawl belongs to; null when the site was removed.
data[].urlstringThe address the crawl started from.
data[].modestringfast (links and status only) or full (SEO data and AI readiness too).
data[].statusstringpending, running, completed, failed, cancelled or partial. New values may appear.
data[].started_viastringWhat started the crawl.One of: dashboard, schedule, mcp, api.
data[].created_atstring or nullWhen the crawl was queued.
data[].started_atstring or nullWhen the crawler started; null while queued.
data[].completed_atstring or nullWhen it ended; null while pending or running.
data[].duration_secondsnumber or nullHow long it ran, once it ended.
data[].max_pagesinteger or nullThe page limit the crawl was started with; null = no limit.
data[].pages_checkedintegerPages read (final once the crawl ended; see progress while it runs).
data[].links_checkedintegerURLs checked, links and files included.
data[].broken_countintegerBroken URLs found.
data[].working_countintegerURLs that worked.
data[].resultsobject or nullThe result buckets stored when the crawl ended; null while it runs and for crawls from before 2026-10-20.
data[].results.totalintegerChecked URLs: ok + redirects + broken + not_checked.
data[].results.okintegerURLs that answered normally.
data[].results.redirectsintegerURLs that redirected.
data[].results.brokenintegerURLs that are broken (404, 5xx, no answer...).
data[].results.not_checkedintegerURLs the crawler could not check (a bot wall, robots.txt, a timeout...); never billed, never broken.
data[].close_reasonstring or nullWhy the crawler stopped: finished, closespider_pagecount, blocked, rate_limited, credit_limit... New values may appear.
data[].crawl_scopeobject or nullWhat the crawl covered (checks on or off, sitemap, render mode, folder, list mode...). Keys may be added.
data[].errorstring or nullWhy a crawl that stopped early stopped, in a sentence; null otherwise.
data[].credits_chargednumber or nullCredits this crawl cost so far; null when nothing was charged (queued, free, or nothing checked).
data[].progressobject or nullLive counters while the crawl is pending or running; null otherwise.
data[].progress.pages_checkedintegerPages read so far.
data[].progress.links_checkedintegerLinks and files checked so far.
data[].progress.broken_countintegerBroken URLs found so far.
data[].progress.max_pagesinteger or nullThe page limit of the crawl; null = no limit.
data[].progress.updated_atstring or nullWhen the crawler last reported (a heartbeat each minute).
data[].processingobjectWhere the crawl's data stands after the crawl itself. A crawl is completed before its pages and fixes are ready.
data[].processing.inventorystringSite Pages (GET /sites/{site_id}/pages) hold this crawl's data. pending = on its way, failed = the sync failed (it is retried for 48 hours), not_applicable = no site of yours takes this crawl in.One of: pending, done, failed, not_applicable.
data[].processing.fixesstringThe fix list was refreshed from this crawl. skipped = the inventory sync failed, so no refresh ran; not_applicable = Fixes are off or no site takes the crawl in. The scan_data_ready webhook is the exact moment.One of: pending, done, skipped, not_applicable.
data[].report_urlstringThe same crawl in the dashboard.
has_morebooleanTrue when another page follows.
next_cursorstring or nullPass as cursor to get the next page; null on the last page.
Example 200 answer of List crawls
{
  "data": [
    {
      "id": "5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90",
      "site_id": 7,
      "url": "https://www.example.com/",
      "mode": "full",
      "status": "completed",
      "started_via": "schedule",
      "created_at": "2026-10-30T02:00:00Z",
      "started_at": "2026-10-30T02:00:04Z",
      "completed_at": "2026-10-30T02:14:51Z",
      "duration_seconds": 887.2,
      "max_pages": 5000,
      "pages_checked": 1840,
      "links_checked": 9215,
      "broken_count": 12,
      "working_count": 9020,
      "results": {
        "total": 9215,
        "ok": 9020,
        "redirects": 160,
        "broken": 12,
        "not_checked": 23
      },
      "close_reason": "finished",
      "crawl_scope": {
        "check_external": false,
        "check_internal": true,
        "crawl_sitemap": true,
        "folder": "",
        "list_mode": false,
        "render_js": false
      },
      "credits_charged": 3680,
      "processing": {
        "inventory": "done",
        "fixes": "done"
      },
      "report_url": "https://dashboard.perfbee.com/#/brokenLinks?scan=5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxfQ.opaque"
}

Problems this call documents

Get one crawl

GET/crawls/{crawl_id}

Scope:
read
Rate class:
light
Operation id:
get_crawl

One crawl: status, counts, what it covered, credits charged, live progress while it runs and processing, which says whether the crawl's pages and fixes are ready yet (a crawl is completed before they are). Add wait (1 to 25 seconds) to hold the call while the crawl is pending or running: it returns as soon as the crawl is over, or after wait seconds with the current state; repeat until status is final. A wait holds one server thread but no database connection; when this server has too many waits open it answers at once with Retry-After instead of waiting. A crawl of another workspace is 404. Free; needs read.

Parameters

NameInTypeDescription
crawl_idpathstringrequiredThe crawl's id (a UUID): from GET /crawls or the answer of POST /crawls.
waitqueryintegeroptionalSeconds to wait for a pending or running crawl to end, 0 to 25.Range: 0 to 25. Default: 0.

Answer 200

The crawl. Retry-After is set when a wait was asked for but this server has too many waits open: the answer is the current state, poll again after that many seconds. Headers: Retry-After.

FieldTypeDescription
idstringThe crawl's id (a UUID).
site_idinteger or nullThe most specific site of yours this crawl belongs to; null when the site was removed.
urlstringThe address the crawl started from.
modestringfast (links and status only) or full (SEO data and AI readiness too).
statusstringpending, running, completed, failed, cancelled or partial. New values may appear.
started_viastringWhat started the crawl.One of: dashboard, schedule, mcp, api.
created_atstring or nullWhen the crawl was queued.
started_atstring or nullWhen the crawler started; null while queued.
completed_atstring or nullWhen it ended; null while pending or running.
duration_secondsnumber or nullHow long it ran, once it ended.
max_pagesinteger or nullThe page limit the crawl was started with; null = no limit.
pages_checkedintegerPages read (final once the crawl ended; see progress while it runs).
links_checkedintegerURLs checked, links and files included.
broken_countintegerBroken URLs found.
working_countintegerURLs that worked.
resultsobject or nullThe result buckets stored when the crawl ended; null while it runs and for crawls from before 2026-10-20.
results.totalintegerChecked URLs: ok + redirects + broken + not_checked.
results.okintegerURLs that answered normally.
results.redirectsintegerURLs that redirected.
results.brokenintegerURLs that are broken (404, 5xx, no answer...).
results.not_checkedintegerURLs the crawler could not check (a bot wall, robots.txt, a timeout...); never billed, never broken.
close_reasonstring or nullWhy the crawler stopped: finished, closespider_pagecount, blocked, rate_limited, credit_limit... New values may appear.
crawl_scopeobject or nullWhat the crawl covered (checks on or off, sitemap, render mode, folder, list mode...). Keys may be added.
errorstring or nullWhy a crawl that stopped early stopped, in a sentence; null otherwise.
credits_chargednumber or nullCredits this crawl cost so far; null when nothing was charged (queued, free, or nothing checked).
progressobject or nullLive counters while the crawl is pending or running; null otherwise.
progress.pages_checkedintegerPages read so far.
progress.links_checkedintegerLinks and files checked so far.
progress.broken_countintegerBroken URLs found so far.
progress.max_pagesinteger or nullThe page limit of the crawl; null = no limit.
progress.updated_atstring or nullWhen the crawler last reported (a heartbeat each minute).
processingobjectWhere the crawl's data stands after the crawl itself. A crawl is completed before its pages and fixes are ready.
processing.inventorystringSite Pages (GET /sites/{site_id}/pages) hold this crawl's data. pending = on its way, failed = the sync failed (it is retried for 48 hours), not_applicable = no site of yours takes this crawl in.One of: pending, done, failed, not_applicable.
processing.fixesstringThe fix list was refreshed from this crawl. skipped = the inventory sync failed, so no refresh ran; not_applicable = Fixes are off or no site takes the crawl in. The scan_data_ready webhook is the exact moment.One of: pending, done, skipped, not_applicable.
report_urlstringThe same crawl in the dashboard.
Example 200 answer of Get one crawl
{
  "id": "5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90",
  "site_id": 7,
  "url": "https://www.example.com/",
  "mode": "full",
  "status": "completed",
  "started_via": "schedule",
  "created_at": "2026-10-30T02:00:00Z",
  "started_at": "2026-10-30T02:00:04Z",
  "completed_at": "2026-10-30T02:14:51Z",
  "duration_seconds": 887.2,
  "max_pages": 5000,
  "pages_checked": 1840,
  "links_checked": 9215,
  "broken_count": 12,
  "working_count": 9020,
  "results": {
    "total": 9215,
    "ok": 9020,
    "redirects": 160,
    "broken": 12,
    "not_checked": 23
  },
  "close_reason": "finished",
  "crawl_scope": {
    "check_external": false,
    "check_internal": true,
    "crawl_sitemap": true,
    "folder": "",
    "list_mode": false,
    "render_js": false
  },
  "credits_charged": 3680,
  "processing": {
    "inventory": "done",
    "fixes": "done"
  },
  "report_url": "https://dashboard.perfbee.com/#/brokenLinks?scan=5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90"
}

Problems this call documents

Get a crawl's AI readiness report

GET/crawls/{crawl_id}/ai-readiness

Scope:
read
Rate class:
heavy
Operation id:
get_crawl_ai_readiness

The AI Search Readiness report of a detailed crawl, as the dashboard shows it: which AI bots can reach the site (from robots.txt), the site-level checks, and exact page counts per issue. A fast crawl and a crawl that never got the report are 404 ai_readiness_unavailable (reason: fast_mode or missing); a crawl that is running, or whose report is still being written, is 409 crawl_not_ready. Free; needs read; counts as a heavy call (the page counts are worked out from the crawl's pages on every call).

Parameters

NameInTypeDescription
crawl_idpathstringrequiredThe crawl's id (a UUID): from GET /crawls or the answer of POST /crawls.

Answer 200

OK. The body is a AiReadiness.

FieldTypeDescription
statusstringready. (A crawl whose report is still being written answers 409 crawl_not_ready.)
botsarray of objectOne row per AI bot: can it reach the site, from robots.txt.
bots[].tokenstringThe bot's robots.txt name.
bots[].vendorstringWho runs it.
bots[].purposestringsearch, user (fetches a page when a person asks) or training.
bots[].feedsstringWhat it feeds (ChatGPT search, AI Overviews...).
bots[].kindstringcrawler, or token (a robots.txt name another crawler obeys).
bots[].robots_bindingbooleanFalse when the vendor says the agent may ignore robots.txt (a Disallow is then a request).
bots[].robots_notestringA note on how the vendor treats robots.txt; may be empty.
bots[].robotsobject or nullWhat the site's robots.txt says for this bot; null when it was not read.
bots[].reachableboolean or nullFalse when robots.txt disallows the homepage for it; null when unknown.
robotsobject or nullThe site's robots.txt as read: issues, Content-Signal lines, CDN hints.
probeobject or nullThe sample-page fetch of the report (sections as stored); may be null.
renderarray of objectSample pages: what the raw HTML holds compared with the rendered page.
siteobject or nullSite-level checks: brand identity, llms.txt and the like (sections as stored).
checked_atstring or nullWhen the report was made.
pagesobject
pages.totalintegerPages that answered 200.
pages.quotableintegerPages without an issue that keeps them out of AI answers.
pages.issuesarray of objectThe issues found, with exact page counts.
pages.issues[].codestringThe issue's stable code (noindex, nosnippet, js_shell...).
pages.issues[].labelstringThe issue in words.
pages.issues[].severitystringerror or warning.
pages.issues[].helpstringWhat it means and how to fix it.
pages.issues[].countintegerPages with the issue (distinct URLs).
Example 200 answer of Get a crawl's AI readiness report
{
  "status": "ready",
  "bots": [
    {
      "token": "OAI-SearchBot",
      "vendor": "OpenAI",
      "purpose": "search",
      "feeds": "ChatGPT search",
      "kind": "crawler",
      "robots_binding": true,
      "robots_note": "",
      "robots": {
        "home_allowed": true
      },
      "reachable": true
    }
  ],
  "robots": {
    "issues": []
  },
  "render": [],
  "site": {
    "llms_txt": false
  },
  "checked_at": "2026-10-30T02:16:10+00:00",
  "pages": {
    "total": 1620,
    "quotable": 1588,
    "issues": [
      {
        "code": "noindex",
        "label": "Not indexable",
        "severity": "error",
        "help": "noindex in the robots meta tag or X-Robots-Tag header.",
        "count": 32
      }
    ]
  }
}

Problems this call documents

Download a crawl's report as CSV or Excel

GET/crawls/{crawl_id}/exports/{slug}.{file_format}

Scope:
read
Rate class:
heavy
Operation id:
export_crawl

The dashboard's export of one crawl, byte for byte the same code: slug is the report (broken-links, all-urls, seo, ai-suggestions, duplicates, css-js, images, extraction, custom-search, orphans, redirect-chains, canonical-chains, elements, and links and anchor-text while the link graph is on), the format is csv or xlsx. Needs a plan with exports (Starter and up), and for ai-suggestions and elements the plan features the dashboard asks for. The whole report is served: no row selection and no filter. A report with more rows than a request may build (250,000 for CSV, 150,000 for Excel) is refused with 413 export_too_large, never cut; a crawl that is still running, or a report this crawl cannot make (links needs link capture), is 409 crawl_not_ready. Counts as a heavy call.

Parameters

NameInTypeDescription
crawl_idpathstringrequiredThe crawl's id (a UUID): from GET /crawls or the answer of POST /crawls.
slugpathstringrequiredThe report to download, for example broken-links: the operation description lists them.
file_formatpathstringrequiredcsv or xlsx.One of: csv, xlsx.

Answer 200

The file, built by the dashboard's own export code: same columns, same file name (Content-Disposition), same content as the dashboard's download. Content type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv.

Problems this call documents

GET/crawls/{crawl_id}/links

Scope:
read
Rate class:
heavy
Operation id:
list_crawl_links

Every URL the crawl checked (pages, links, images, scripts...), in crawl order, paged with cursor: the status, whether it is broken, why it was not checked, its type, depth, how many pages link to it and where a redirect leads. Filters: is_broken, resource_type, status (the word) and status_code. include=count adds total (one extra count). v1 lists no source pages per link. A crawl that is still pending or running answers 409 crawl_not_ready. Free; needs read; counts as a heavy call.

Parameters

NameInTypeDescription
crawl_idpathstringrequiredThe crawl's id (a UUID): from GET /crawls or the answer of POST /crawls.
is_brokenquerybooleanoptionalTrue: only broken URLs. False: everything else.
resource_typequerystringoptionalOnly this kind of URL.One of: link, image, css, js, video, iframe, audio, embed, object.
statusquerystringoptionalOnly this status word.One of: working, redirect, broken, not_checked.
status_codequeryintegeroptionalOnly this HTTP status code.Range: 100 to 599.
includequeryanyoptionalcount adds total: how many URLs match.
limitqueryintegeroptionalItems per page, 1 to 200.Range: 1 to 200. Default: 50.
cursorquerystringoptionalThe next_cursor of the previous page. Opaque: never build one.

Answer 200

OK. The body is a LinkPage.

FieldTypeDescription
dataarray of objectThe items of this page, in the endpoint's fixed order.
data[].idintegerThe row's id, ascending in crawl order. Lists are paged by it; never build a cursor from it.
data[].urlstringThe URL that was checked.
data[].status_codeinteger or nullThe HTTP status; null when there was no answer.
data[].statusstringworking, redirect (3xx), broken, or not_checked (the crawler could not check it: see block_reason).One of: working, redirect, broken, not_checked.
data[].is_brokenbooleanTrue when the crawler found it broken.
data[].block_reasonstring or nullWhy a not_checked URL was not checked (bot_protection, rate_limited, access_denied, robots_txt...).
data[].resource_typestring or nulllink, image, css, js, video, iframe, audio, embed or object.
data[].link_typestring or nullinternal, external, or out_of_scope (on your host, outside the folder the crawl stayed in).
data[].depthinteger or nullClicks from the start page; null when no crawled page links to it.
data[].inbound_linksintegerHow many pages of the crawl link to it.
data[].redirect_targetstring or nullWhere a redirect leads.
data[].errorstring or nullThe crawler's short note on a failed check.
data[].response_time_msnumber or nullHow long the answer took.
has_morebooleanTrue when another page follows.
next_cursorstring or nullPass as cursor to get the next page; null on the last page.
totalinteger or nullHow many URLs match the filters; only with include=count, otherwise null.
Example 200 answer of List the URLs a crawl checked
{
  "data": [
    {
      "id": 48213,
      "url": "https://www.example.com/old-guide",
      "status_code": 404,
      "status": "broken",
      "is_broken": true,
      "resource_type": "link",
      "link_type": "internal",
      "depth": 2,
      "inbound_links": 14,
      "response_time_ms": 182.4
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxfQ.opaque"
}

Problems this call documents

List your sites

GET/sites

Scope:
read
Rate class:
light
Operation id:
list_sites

Your workspace's sites (My Sites in the dashboard), by id, each with its newest crawl, its next scheduled crawl, whether you verified the host, its crawl profile and whether the fast lane is on. Paged with cursor. Secrets of the site (crawl token, RUM token) are never part of the answer. Free; needs read.

Parameters

NameInTypeDescription
limitqueryintegeroptionalItems per page, 1 to 200.Range: 1 to 200. Default: 50.
cursorquerystringoptionalThe next_cursor of the previous page. Opaque: never build one.

Answer 200

OK. The body is a SiteList.

FieldTypeDescription
dataarray of objectThe items of this page, in the endpoint's fixed order.
data[].idintegerThe site's id (64-bit integer): pass it as site_id.
data[].urlstringThe address the site was added with.
data[].namestring or nullThe name given to the site, if any.
data[].hoststringThe site's host without www.: www and the bare domain are one site.
data[].crawl_profilestringstandard, or allowlisted: PerfBee asked the site's owner to let our crawler in and crawls it fast.
data[].verifiedbooleanTrue when your workspace proved it controls the host (Settings > My Sites).
data[].owned_ratebooleanTrue when crawls of this site are billed at the half owned-site rate (verified or allowlisted).
data[].fast_lanebooleanTrue when the owner's self-serve fast lane is on for this verified host.
data[].last_crawlobject or nullThe newest crawl of this site, whatever its status; null before the first.
data[].last_crawl.idstringThe crawl's id (a UUID): pass it to GET /crawls/{crawl_id}.
data[].last_crawl.urlstringThe address the crawl started from.
data[].last_crawl.statusstringpending, running, completed, failed, cancelled or partial. New values may appear.
data[].last_crawl.modestringfast (links and status only) or full (SEO data and AI readiness too).
data[].last_crawl.started_atstring or nullWhen the crawler started; null while the crawl waits in the queue.
data[].last_crawl.completed_atstring or nullWhen the crawl ended; null while it is pending or running.
data[].last_crawl.pages_checkedintegerPages the crawl read.
data[].last_crawl.broken_countintegerBroken URLs the crawl found.
data[].last_crawl.report_urlstringThe same crawl in the dashboard.
data[].scheduleobject or nullThe site's next scheduled crawl; null when none is active.
data[].schedule.frequencystringdaily, weekly or monthly.
data[].schedule.modestringfast or full: the mode the scheduled crawl runs in.
data[].schedule.next_run_atstring or nullWhen it runs next; null while it is paused.
data[].schedule.hourinteger or nullHour of the day it runs, 0 to 23, in the schedule's own time zone.
data[].schedule.minuteintegerMinute of the hour, 0 to 59.
data[].schedule.dayinteger or nullDay of the week (0 to 6) for weekly, of the month (1 to 31) for monthly.
data[].schedule.timezonestring or nullIANA time zone of hour and day; null = a fixed offset.
data[].schedule.timezone_offset_minutesintegerThe fixed offset from UTC in minutes, used when timezone is null.
data[].created_atstring or nullWhen the site was added.
has_morebooleanTrue when another page follows.
next_cursorstring or nullPass as cursor to get the next page; null on the last page.
Example 200 answer of List your sites
{
  "data": [
    {
      "id": 7,
      "url": "https://www.example.com/",
      "name": "Example shop",
      "host": "example.com",
      "crawl_profile": "standard",
      "verified": true,
      "owned_rate": true,
      "fast_lane": false,
      "last_crawl": {
        "id": "5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90",
        "url": "https://www.example.com/",
        "status": "completed",
        "mode": "full",
        "started_at": "2026-10-30T02:00:04Z",
        "completed_at": "2026-10-30T02:14:51Z",
        "pages_checked": 1840,
        "broken_count": 12,
        "report_url": "https://dashboard.perfbee.com/#/brokenLinks?scan=5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90"
      },
      "schedule": {
        "frequency": "weekly",
        "mode": "full",
        "next_run_at": "2026-11-06T02:00:00Z",
        "hour": 2,
        "minute": 0,
        "day": 4,
        "timezone": "Europe/Berlin",
        "timezone_offset_minutes": 0
      },
      "created_at": "2026-09-12T10:21:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxfQ.opaque"
}

Problems this call documents

Get one site

GET/sites/{site_id}

Scope:
read
Rate class:
light
Operation id:
get_site

One of your sites with its newest crawl, schedule, verification, crawl profile and fast-lane flag. A site of another workspace, a removed site and an unknown id are all 404. Free; needs read.

Parameters

NameInTypeDescription
site_idpathintegerrequiredThe site's id (from GET /sites).Range: 1 to 2147483647.

Answer 200

OK. The body is a Site.

FieldTypeDescription
idintegerThe site's id (64-bit integer): pass it as site_id.
urlstringThe address the site was added with.
namestring or nullThe name given to the site, if any.
hoststringThe site's host without www.: www and the bare domain are one site.
crawl_profilestringstandard, or allowlisted: PerfBee asked the site's owner to let our crawler in and crawls it fast.
verifiedbooleanTrue when your workspace proved it controls the host (Settings > My Sites).
owned_ratebooleanTrue when crawls of this site are billed at the half owned-site rate (verified or allowlisted).
fast_lanebooleanTrue when the owner's self-serve fast lane is on for this verified host.
last_crawlobject or nullThe newest crawl of this site, whatever its status; null before the first.
last_crawl.idstringThe crawl's id (a UUID): pass it to GET /crawls/{crawl_id}.
last_crawl.urlstringThe address the crawl started from.
last_crawl.statusstringpending, running, completed, failed, cancelled or partial. New values may appear.
last_crawl.modestringfast (links and status only) or full (SEO data and AI readiness too).
last_crawl.started_atstring or nullWhen the crawler started; null while the crawl waits in the queue.
last_crawl.completed_atstring or nullWhen the crawl ended; null while it is pending or running.
last_crawl.pages_checkedintegerPages the crawl read.
last_crawl.broken_countintegerBroken URLs the crawl found.
last_crawl.report_urlstringThe same crawl in the dashboard.
scheduleobject or nullThe site's next scheduled crawl; null when none is active.
schedule.frequencystringdaily, weekly or monthly.
schedule.modestringfast or full: the mode the scheduled crawl runs in.
schedule.next_run_atstring or nullWhen it runs next; null while it is paused.
schedule.hourinteger or nullHour of the day it runs, 0 to 23, in the schedule's own time zone.
schedule.minuteintegerMinute of the hour, 0 to 59.
schedule.dayinteger or nullDay of the week (0 to 6) for weekly, of the month (1 to 31) for monthly.
schedule.timezonestring or nullIANA time zone of hour and day; null = a fixed offset.
schedule.timezone_offset_minutesintegerThe fixed offset from UTC in minutes, used when timezone is null.
created_atstring or nullWhen the site was added.
Example 200 answer of Get one site
{
  "id": 7,
  "url": "https://www.example.com/",
  "name": "Example shop",
  "host": "example.com",
  "crawl_profile": "standard",
  "verified": true,
  "owned_rate": true,
  "fast_lane": false,
  "last_crawl": {
    "id": "5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90",
    "url": "https://www.example.com/",
    "status": "completed",
    "mode": "full",
    "started_at": "2026-10-30T02:00:04Z",
    "completed_at": "2026-10-30T02:14:51Z",
    "pages_checked": 1840,
    "broken_count": 12,
    "report_url": "https://dashboard.perfbee.com/#/brokenLinks?scan=5b0a5e4c-2f1e-4b8e-9c47-1d3f6a7b8c90"
  },
  "schedule": {
    "frequency": "weekly",
    "mode": "full",
    "next_run_at": "2026-11-06T02:00:00Z",
    "hour": 2,
    "minute": 0,
    "day": 4,
    "timezone": "Europe/Berlin",
    "timezone_offset_minutes": 0
  },
  "created_at": "2026-09-12T10:21:00Z"
}

Problems this call documents