From sitemap.xml to a full audit report
Most audits start with a crawl. That is usually the wrong first move: a crawl discovers what the crawler can reach, not what the site actually publishes. A sitemap is a better seed — the site's own list of canonical URLs, usually one HTTP request away. This tutorial runs a sitemap-to-report workflow with the Sitemap Audit Actor: one audit item per URL, then a CSV export. What the Actor does The Sitemap Audit Actor: Fetches the sitemap URL. Parses entries. If the document is a sitemap index, follows its child sitemaps (up to 10 per index). Fetches each page as static HTML and runs the same audit engine as the Bulk Website Audit Actor. Pushes one dataset item per URL with a score, issue list, and page statistics. It does not render JavaScript, crawl links, or check robots.txt. It audits exactly the URLs listed in the sitemap, up to maxUrls. That makes it predictable: the input list is the scope. Step 1 — Prepare the input The input is two fields: { "sitemapUrl": "https://example.com/sitemap.xml", "maxUrls": 100 } sitemapUrl (required): the public URL of the sitemap or sitemap index. maxUrls (optional, default 100): the maximum number of page URLs to audit. Start with a small maxUrls. A sitemap that returns an HTML error page, a login wall, or a compressed file fails early, and you want to know that in 30 seconds, not 30 minutes. Step 2 — Run it through the API You need an Apify API token in an environment variable — not in a notebook or a commit. export APIFY_TOKEN="your_token_here" curl -sS -X POST \ "https://api.apify.com/v2/acts/D49xJAuq6tledUTzL/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "sitemapUrl": "https://example.com/sitemap.xml", "maxUrls": 100 }' \ -o audit.json The run-sync-get-dataset-items endpoint starts the run, waits for it to finish, and returns the dataset items as JSON. It is the shortest path for small and medium sitemaps. The synchronous endpoint has a platform timeout (around five minutes), so keep maxUrls small; for larger sitemaps, use the asynchronous pattern: # Start the run curl -sS -X POST \ "https://api.apify.com/v2/acts/D49xJAuq6tledUTzL/runs?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"sitemapUrl":"https://example.com/sitemap.xml","maxUrls":1000}' # Poll the run, then fetch its default dataset curl -sS \ "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json&token=$APIFY_TOKEN" \ -o audit.json The run response contains id and defaultDatasetId. Poll /v2/actor-runs/{id} until SUCCEEDED, then fetch the dataset. Step 3 — Read the output Each dataset item describes one URL: Field Type Meaning url string Final URL after redirects status number or null HTTP status; null on a network error score number Heuristic 0–100 triage score title string Page lang string or null value issues array Issue objects with code, severity, message, count, examples stats object Counts for words, images, links, form fields, headings, JSON-LD blocks The stats object: "stats": { "words": 812, "images": 14, "images_without_alt": 1, "links": 96, "empty_links": 3, "form_fields": 6, "unlabeled_fields": 0, "headings": 24, "json_ld_blocks": 1 } Do not trust score without checking status: a 403 challenge page can still parse as HTML and produce a misleading score. Filter to status == 200 and reject challenge titles such as "Just a moment..." or "Access Denied". Step 4 — Export CSV From the Apify console, open the run, go to the dataset, and use Export → CSV. From the API: curl -sS \ "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=csv&token=$APIFY_TOKEN" \ -o audit.csv Top-level fields export cleanly. Nested issues and stats may arrive as JSON strings or flattened keys; for spreadsheet work, build the columns you need from JSON with jq: curl -sS \ "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json&token=$APIFY_TOKEN" \ | jq -r ' ["url","status","score","words","images","images_without_alt","form_fields","unlabeled_fields"], (.[] | [.url, .status, .score, .stats.words, .stats.images, .stats.images_without_alt, .stats.form_fields, .stats.unlabeled_fields]) | @csv' > audit.csv That gives you a stable schema you can open in any spreadsheet or load into a database. A repeatable workflow Run with maxUrls: 20 to validate the sitemap and see whether the site blocks the Actor. Raise maxUrls to the real size, or split the sitemap by section. Filter status == 200 and remove challenge pages. Group by issue code to get prevalence, and sum stats for element-level rates. Schedule the Actor weekly and store each dataset to track regressions. The last step matters: scheduling turns a one-off report into a trend. Caveats Static HTML only. No JavaScript execution. Client-rendered pages may return an empty shell or missing content; treat that as a rendering limitation, not an accessibility finding. Sitemap indexes are capped. The Actor follows up to 10 child sitemaps per index and takes the first maxUrls URLs it finds. Large sitemaps will be truncated; split them or run multiple inputs. No lastmod filtering. Every is treated equally. To audit only recently changed pages, filter the sitemap first. No robots.txt check. The Actor fetches each URL directly. Keep maxUrls sane, avoid hammering production, and schedule runs outside peak hours. Targets may still return 429 or 403; those items are not valid results. Compressed sitemaps are not explicitly handled. Plain XML is the safe input. If your sitemap is served as .xml.gz, decompress it first or expose an uncompressed variant. The score is a triage signal. It weights template-level issues such as missing titles, canonical links, labels, and alt text. It is not a WCAG conformance score and cannot detect contrast, keyboard traps, focus order, or runtime ARIA problems. Wrap-up The sitemap is the scope; the Actor is the executor; the dataset is the report. Once the pipeline works, the hard part is deciding which recurring issue codes your team will actually fix. Run the workflow with the Sitemap Audit Actor. For arbitrary URL lists, use the Bulk Website Audit Actor.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to