A practical workflow for agencies that need a full-site baseline before a manual review.
The problem
A client sends you a website with 1,000 pages and asks: "Is it accessible? Is the SEO clean?"
You open the first page. You check the title, the meta description, the images, the form labels, the heading structure. You do it again on page two. By page ten you already know you will not finish today.
Manual page-by-page audits do not scale. What scales is a triage: run the obvious checks on every URL, sort by severity, and spend your human time on the pages that actually need it.
The tool
I use two Apify Actors for this:
- Website Accessibility & SEO Audit — crawls one site and audits its pages.
- Bulk Website Audit — takes a list of URLs and audits each one in a single run.
Both return one JSON item per page, with a score and a list of concrete issues.
What it checks
For each page:
-
<title>presence and length - meta description presence and length
-
langon<html> -
<h1>presence, multiple H1s, heading-level skips - images without
alt - links without accessible text
- form fields without an associated label
- viewport meta
noindex- canonical link
- thin content (< 200 words)
- JSON-LD structured data
It is a static check. It does not replace a manual WCAG 2.2 audit, but it removes the boring part.
Step 1 — Build the URL list
You can start from:
- a
sitemap.xml(use the Sitemap Audit Actor to extract and audit URLs directly), - a CSV of landing pages,
- a list of client sites.
For this example, a JSON array is enough:
{ "urls": [ "https://example.com", "https://example.com/about", "https://example.com/contact" ] } Step 2 — Run the Actor
From the Apify Console, paste the JSON and click Start.
From the API:
curl -X POST "https://api.apify.com/v2/acts/calimero-projects~bulk-website-audit/runs?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"urls":["https://example.com","https://example.com/about"]}' Or with the Python client:
from apify_client import ApifyClient client = ApifyClient("<YOUR_TOKEN>") run = client.actor("calimero-projects/bulk-website-audit").call( run_input={"urls": ["https://example.com", "https://example.com/about"]} ) for item in client.dataset(run["defaultDatasetId"]).iterate_items(): print(item["url"], item["score"], len(item["issues"])) Step 3 — Read the output
One item per page:
{ "url": "https://example.com/", "status": 200, "score": 65, "title": "Example Domain", "issues": [ { "code": "missing_meta_description", "severity": "medium", "message": "Missing meta description", "count": 1, "examples": [] } ], "stats": { "words": 27, "images": 0, "images_without_alt": 0, "links": 0, "empty_links": 0, "form_fields": 0, "unlabeled_fields": 0, "headings": 0 } } Export the dataset as CSV or Excel and you have a client-ready baseline in minutes.
Step 4 — Triage, not fix
Sort by score. Ignore the pages above 90 unless the client asked for perfection. Start from the pages with:
- missing
alton images, - form fields without labels,
- missing or multiple
<h1>, -
noindexon pages that should rank.
These are template-level issues: fix the template, fix hundreds of pages at once.
What it costs
The Bulk Website Audit uses pay-per-event pricing:
- $0.07 per run (start event, 1 GB memory)
- $0.01 per audited page
A 1,000-page audit costs about $10.07. An agency can bill that as a few minutes of work.
What it does not do
- It does not render JavaScript. For SPA-heavy sites, use a browser-based check.
- It does not test contrast, keyboard navigation or screen-reader behaviour.
- It does not decide whether a site is legally compliant.
It tells you where to look first. That is usually the difference between a two-day audit and a two-hour triage.
Try it
If you run it on a client site, I would like to know which issues show up most often. That is the data I use to decide what the next version should check.
