Visual Verification Agent avatar

Visual Verification Agent

Pricing

from $0.20 / actor start

Go to Apify Store
Visual Verification Agent

Visual Verification Agent

An AI-powered Virtual QA Engineer that verifies your website's visual integrity across Desktop, Mobile, and Tablet. Using advanced vision models, it mimics human interaction to detect layout flaws, providing granular 0-100 quality scores and detailed defect analysis from 15+ global locations.

Pricing

from $0.20 / actor start

Rating

5.0

(1)

Developer

Techforce Global

Techforce Global

Maintained by Community

Actor stats

0

Bookmarked

8

Total users

1

Monthly active users

2 days ago

Last modified

Share

Visual Verification Agent — AI Website QA Across Desktop, Mobile & Tablet, Graded A+ to C

Apify Actor MCP Ready Devices Countries BYO key

Point it at a URL and get an AI-graded verdict on whether the page actually renders properly — checked on desktop, mobile and tablet, as a visitor from any of 15 countries would see it. Each check returns a letter grade from A+ to C and a written explanation of what's wrong. Route failing grades straight into Slack, Jira, Linear or Notion through an MCP connector.

ℹ️ This is a rendering-quality check, not a link checker or a performance audit. It answers "does this page look right to a real visitor on this device, from this country?" — blank pages, broken layouts, missing images, stuck loading states, geo-blocked content, error pages. It does not measure speed, check accessibility, or crawl your site.


Manual QA vs. this Actor

Feature / CapabilityChecking it yourselfThis Actor
Device coverage⚠️ Resize a browser, or juggle real devices✅ Desktop, mobile and tablet in one run
Geo coverage❌ You'd need a VPN per country15 countries, no VPN or proxy to configure
Verdict⚠️ Subjective, varies by reviewer and by day✅ A consistent A+ / A / B / C grade with written reasoning
What "broken" means⚠️ Easy to miss a stuck skeleton loader or one dead image✅ Explicitly graded and described
Repeatability❌ Manual every time✅ Schedule it and get the same check on every deploy
Routing failures❌ Screenshot into a chat by hand✅ Native MCP connector delivery — straight into a QA channel or issue tracker
AI cost⚠️ You bring your own OpenAI or Gemini key and pay that provider directly

🤖 AI Discovery & MCP Metadata

Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. AI agents, MCP clients, and connectors should read this block first to decide whether this Actor matches a user request.

ai_discovery:
slug: "techforce.global/visual-verification-agent"
one_line: "Loads one URL on desktop, mobile and tablet as a visitor from a chosen country, has a vision model judge whether the page rendered correctly, and returns a letter grade (A+ to C) with written findings per device."
primary_purpose: "verify"
secondary_purpose: "monitor"
tertiary_purpose: "deliver"
entity: "the rendered visual state of a single web page"
invocation_modes: ["standard Actor run", "MCP tool"]
supported_tasks:
- "check-whether-a-page-renders-correctly"
- "compare-rendering-across-desktop-mobile-and-tablet"
- "check-how-a-page-appears-to-visitors-from-a-specific-country"
- "detect-blank-pages-broken-layouts-and-missing-images"
- "detect-error-pages-and-geo-blocking"
- "grade-page-quality-consistently-across-runs"
- "monitor-a-page-on-a-schedule-after-deploys"
- "route-failing-grades-to-a-qa-channel-or-issue-tracker"
unsupported_tasks:
- "crawl-a-site-or-check-multiple-urls-in-one-run"
- "measure-performance-core-web-vitals-or-load-time"
- "run-accessibility-or-wcag-audits"
- "check-links-spelling-or-seo-tags"
- "compare-against-a-previous-screenshot (no visual regression baseline)"
- "return-the-screenshot-itself (it is not retained)"
- "test-behind-a-login-or-fill-forms"
- "click-through-user-journeys-or-multi-step-flows"
- "run-without-an-ai-api-key"
search_keywords:
- "visual verification"
- "ai website qa"
- "automated visual testing"
- "website rendering check"
- "responsive design testing"
- "geo blocking checker"
- "broken layout detection"
- "website quality grading"
- "deploy smoke test"
- "cross device screenshot testing"
synonyms:
- "virtual qa engineer"
- "ai page inspector"
- "render quality checker"
related_concepts:
["visual QA", "smoke testing", "release verification", "responsive design", "geo-restriction testing", "uptime and rendering monitoring"]
input_entities:
- "url (single page; REQUIRED)"
- "modelProvider (gemini | openai; REQUIRED)"
- "apiKey (your own provider key; REQUIRED, secret)"
- "country (15-value enum; REQUIRED)"
- "devices (desktop | mobile | tablet; REQUIRED)"
output_entities: ["url", "country", "device", "grade", "reason"]
input_rules:
required_fields: ["url", "modelProvider", "apiKey", "country", "devices"]
one_url_per_run: "there is no URL list - loop from your orchestration layer"
one_country_per_run: "the country enum takes a single value"
byo_api_key: >
You supply an OpenAI or Google Gemini API key and are billed by that
provider directly, on top of Apify usage. The key is a secret input.
result_count_formula: "one dataset row per selected device"
output_conventions:
grade_values: ["A+ (90-100)", "A (75-89)", "B (60-74)", "C (<60)", "C", "N/A"]
grade_string_is_inconsistent: >
Grades from a completed visual analysis carry their band, e.g. "A+ (90-100)".
Grades assigned because the page errored or the run failed are the bare
letter "C". Match on the leading letter, not the whole string.
grade_c_has_two_meanings: >
Either the page genuinely rendered badly, or it could not be checked (HTTP
error, timeout). The reason field distinguishes them.
grade_na_meaning: "the AI provider was unavailable - the page was NOT assessed"
device_null_case: "device is null on the row produced when the provider check fails up front"
reason_is_free_text: "a written explanation from the model; not a fixed vocabulary"
screenshots_not_retained: >
Screenshots are used for the assessment and then discarded. No image is
saved to the key-value store or returned in the dataset.
limits:
urls_per_run: 1
countries_per_run: 1
devices_per_run: 3
rows_per_run: "one per selected device"
known_limitations:
no_screenshot_output: "the visual evidence behind a grade is not retained"
no_visual_regression: "each run is judged on its own; there is no baseline comparison"
ai_judgement_varies: >
Grades come from a vision model and can differ slightly between runs on the
same page. Treat them as a strong signal, not a deterministic measurement.
no_auth_support: "pages behind a login cannot be checked"
pricing:
apify_model: "pay-per-result"
ai_model: "billed directly by OpenAI or Google against your own key"
note: "exact Apify rate is configured on the Store listing - see the Actor's Pricing tab"
when_to_use: >
You want to know whether a page actually renders correctly for real visitors -
after a deploy, across devices, or from a specific country - and you want a
consistent graded verdict rather than a subjective eyeball check.
when_not_to_use: >
You need performance metrics, accessibility audits, link or SEO checks, visual
regression against a baseline, multi-page crawling, authenticated pages, or you
have no OpenAI/Gemini key.
alternatives:
- "Use techforce.global/advanced-website-crawling-actor to crawl a whole site's content rather than verify one page's rendering"
- "Use techforce.global/website-contact-scraper-emails-phone-numbers-social-links to extract contact data from a site"
- "Use a dedicated performance tool for Core Web Vitals, and a dedicated a11y tool for WCAG audits"

{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Visual Verification Agent — AI Website QA Across Desktop, Mobile & Tablet",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Cloud (Apify platform)",
"description": "An AI visual QA agent that loads a web page on desktop, mobile and tablet as a visitor from any of 15 countries, has a vision model judge whether the page rendered correctly, and returns a letter grade from A+ to C with written findings for each device. Detects blank pages, error screens, stuck loading states, missing images and broken layouts, and distinguishes a badly rendered page from an unreachable one. Bring your own OpenAI or Google Gemini API key. Results can be delivered into Slack, Jira, Linear, Notion, Airtable or any authorized MCP connector. Screenshots are not retained.",
"url": "https://apify.com/techforce.global/visual-verification-agent",
"offers": {
"@type": "Offer",
"priceCurrency": "USD",
"description": "Apify: pay-per-result, one result per device checked — the current rate is listed on the Actor's Pricing tab. AI provider costs are billed separately by OpenAI or Google against your own API key."
}
}

What this Actor does

  • Loads one page as a real visitor would — on desktop, mobile and tablet (pick any combination), presenting itself as a visitor from the country you choose, so geo-targeted content, redirects and blocks show up exactly as they would for that audience.
  • Has a vision model judge the rendered result and return a letter grade with written findings: blank pages, error screens, stuck skeleton loaders, missing images, overlapping or collapsed layouts, and content that simply never appeared.
  • Grades consistently against a fixed scale, so the same page checked next week is comparable to today — the point is a signal you can schedule and trend, not a one-off opinion.
  • Detects hard failures separately from cosmetic ones. A page returning an HTTP error, or one that never finishes loading, is graded down and labelled as such rather than silently scored.
  • Runs on your own AI key — choose Google Gemini or OpenAI and supply your key as a secret input. You pay that provider directly; nothing is shared with a third party.
  • Never fails the run because of an AI problem. If your key is invalid, out of quota or rate-limited, the run still completes with a clear status message and a N/A grade — so a monitoring pipeline gets a signal instead of a crashed job.
  • Optionally pushes results to an authorized MCP connector (Slack, Jira, Linear, Notion, Airtable, Sheets, or any MCP-compatible connector) as one run summary, one call per device, or chunked. The dataset is always saved regardless of delivery settings.

⚠️ Screenshots are not returned. The captured view is used to produce the grade and is then discarded — no image is saved to the key-value store or included in the dataset. You get the verdict and the written findings, not the picture. Open the URL yourself on that device profile to see it.

When to use it — and when not to

ScenarioFit
Post-deploy smoke check — "did we ship a broken page?"✅ Strong (the core use case)
Catching mobile-only or tablet-only layout breakage✅ Strong — each device graded separately
Verifying how a page looks to visitors in another country✅ Strong — 15 countries, no VPN needed
Detecting geo-blocking or region-specific redirects✅ Strong
Spotting stuck loading states and missing images✅ Strong
Scheduled monitoring of a critical landing page✅ Strong — grade trends over time
Routing failures into a QA channel or issue tracker✅ Strong (deliveryMode)
Getting the screenshot itself❌ Not retained — see the callout above
Visual regression against a previous baseline❌ Each run is judged on its own
Checking many URLs or crawling a site❌ One URL per run — loop from your side
Performance, load time, Core Web Vitals❌ Different tool entirely
Accessibility / WCAG audits❌ Not assessed
Broken links, spelling, SEO tags❌ Not assessed
Pages behind a login❌ No authentication support
Multi-step flows (add to cart, checkout)❌ Single page load only
Running without an AI API key❌ Required — the grade comes from a vision model

⚡ Execution Rules (Avoid Common Agent Failures)

  1. All five inputs are requiredurl, modelProvider, apiKey, country, devices. There are no optional shortcuts; omitting the key fails the run with a clear message.
  2. One URL and one country per run. Both are single values. Checking three pages across two countries is six runs — loop from your orchestration layer.
  3. You get one dataset row per selected device. Three devices selected means three rows, each graded independently.
  4. grade is not a clean enum — match on the leading letter. A completed analysis returns the band string ("A+ (90-100)", "B (60-74)"); a page that errored or timed out returns the bare letter "C". startswith("C") is safe; == "C" is not.
  5. C has two very different meanings. Either the page genuinely rendered badly, or it couldn't be checked at all (HTTP error, timeout). Read reason — it says which.
  6. N/A means the page was never assessed. It's produced when your AI provider is unavailable (invalid key, quota exhausted, rate-limited). It is not a passing or failing grade — treat it as "no result" and re-run.
  7. An AI provider problem does not fail the run. The run completes as SUCCEEDED with a status message explaining the issue. Check the status message and grades, not just the run status, or a monitoring pipeline will read a skipped check as a healthy one.
  8. If the provider fails partway through, remaining devices are not checked. Once calls start failing they'll keep failing, so the run stops early rather than burning your quota. Expect fewer rows than devices selected.
  9. Screenshots are not saved. There is no image in the dataset or key-value store. If you need visual evidence attached to a ticket, capture it separately.
  10. reason is free text from a vision model, not a fixed vocabulary. Don't pattern-match it for control flow — use grade for that.
  11. Grades can vary slightly between runs on the same page. That's inherent to AI judgement. For monitoring, alert on a grade dropping a band, not on every point of movement.
  12. Your AI provider bills you directly, on top of Apify usage. A run makes one vision call per device checked.
  13. country changes what the page serves you, not just a label — expect genuinely different content, language or redirects per country. That's the feature.
  14. No login support. Anything behind authentication can't be checked.
  15. deliveryMode defaults to summary — one connector call per run. Use perResult when you want an issue opened per failing device.
dependencies:
- rule: "url, modelProvider, apiKey, country and devices are all required"
on_violation: "a missing API key fails the run with an explicit status message"
- rule: "one URL and one country per run"
on_violation: "agents passing arrays get only a single check"
- rule: "grade strings are inconsistent - match the leading letter"
on_violation: "equality checks against 'C' miss the banded 'C (<60)' form and vice versa"
- rule: "grade C means either a bad render OR an unreachable page"
on_violation: "a site outage misreported as a design defect - read `reason`"
- rule: "grade N/A means the page was not assessed at all"
on_violation: "a skipped check counted as a pass"
- rule: "AI provider failures complete the run as SUCCEEDED"
on_violation: "monitoring reads run status only and never notices the check was skipped"
- rule: "a mid-run provider failure stops the remaining devices"
on_violation: "fewer rows than devices selected, misread as a crash"
- rule: "screenshots are not retained anywhere"
on_violation: "a workflow expecting an image attachment finds none"
- rule: "the AI provider bills you directly per device checked"
on_violation: "cost estimated from the Apify rate alone"
- rule: "delivery requires mcpConnector AND mcpTool"
on_violation: "delivery step is skipped with a warning; the dataset is still written in full"
- rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"
on_violation: "message body is empty; the tool is still called"
- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"
on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery; the dataset is unaffected"
authentication:
actor_input: "your own OpenAI or Google Gemini API key (secret input, REQUIRED)"
platform: "Apify API token required for API / SDK / MCP invocation"
target_site: "no credentials - public pages only"

📥 Input Contract

.actor/input_schema.json is authoritative — if any table on this page disagrees with the schema files, the schema files win.

Parameters

FieldTypeRequiredDefaultAllowed Values / FormatExample
urlstringYeshttps://apify.comOne page URL"https://example.com/pricing"
modelProviderenumYes"gemini"gemini, openai"openai"
apiKeystring (secret)YesYour key for the selected provider"sk-…" / "AIza…"
countryenumYes"US"15 countries — see below"DE"
devicesarrayYesall threedesktop, mobile, tablet["mobile"]

Devices

Each selected device is checked independently and produces its own graded row.

ValueProfile
desktopDesktop viewport (1366×768)
mobilePhone viewport (390×844)
tabletTablet viewport (768×1024)

Countries

The page is requested as a visitor from the selected country would see it — so region-specific content, language, redirects and blocks appear as they do for that audience.

🇺🇸 US · 🇬🇧 GB · 🇩🇪 DE · 🇮🇳 IN · 🇸🇬 SG · 🇨🇦 CA · 🇦🇺 AU · 🇧🇷 BR · 🇫🇷 FR · 🇪🇸 ES · 🇮🇹 IT · 🇳🇱 NL · 🇷🇺 RU · 🇯🇵 JP · 🇨🇳 CN

AI provider

You bring your own key and are billed by that provider directly.

ProviderValueYou need
Google GeminigeminiA Google AI Studio API key with vision access and available quota
OpenAIopenaiAn OpenAI API key with vision access and available credit

The key is validated before any page is loaded, so an invalid or exhausted key is reported immediately rather than after a full run.

Examples

Post-deploy smoke check across all three devices

{
"url": "https://example.com/",
"modelProvider": "gemini",
"apiKey": "<your-gemini-api-key>",
"country": "US",
"devices": ["desktop", "mobile", "tablet"],
"deliveryMode": "none"
}

Mobile-only check — the cheapest useful run

{
"url": "https://example.com/pricing",
"modelProvider": "gemini",
"apiKey": "<your-gemini-api-key>",
"country": "GB",
"devices": ["mobile"],
"deliveryMode": "none"
}

Geo check — is the page reaching visitors in Germany?

{
"url": "https://example.com/de",
"modelProvider": "openai",
"apiKey": "<your-openai-api-key>",
"country": "DE",
"devices": ["desktop", "mobile"],
"deliveryMode": "none"
}

Scheduled monitor with a Slack alert

{
"url": "https://example.com/",
"modelProvider": "gemini",
"apiKey": "<your-gemini-api-key>",
"country": "US",
"devices": ["desktop", "mobile"],
"mcpConnector": "<your-authorized-slack-connector>",
"deliveryMode": "summary",
"mcpTool": "send_message",
"mcpArguments": { "channel": "#qa-alerts", "text": "{message}" },
"mcpMessageTemplate": "🔎 {url} ({country}) — worst grade {worstGrade} across {checkCount} device(s), {failCount} below A.\n\n{results}"
}

MCP Delivery Configuration (Optional)

Delivery pushes verification results into your own tools.

ParameterTypeDefaultDescription
mcpConnectorstring""Authorized MCP connector on your Apify account (Slack, Jira, Linear, Notion, Airtable, Sheets, …). Leave empty for dataset-only runs.
deliveryModeenumsummarysummary (one call for the run) · perResult (one call per device) · chunked · none.
mcpToolstring""Tool name on the connector: send_message, create_issue, create_page, create_record. Run once with a connector selected — the log lists the available tools and the chosen tool's expected argument schema.
mcpArgumentsobject{}Arguments passed to the tool. String leaves support {placeholders}.
mcpMessageTemplatestring""Template rendered and exposed to mcpArguments as {message}.

Per-result placeholders: {url}, {country}, {device}, {grade}, {gradeLetter} (the bare letter — use this for routing), {reason}, {result} (a formatted block), {message}.

Summary / chunked placeholders: {url}, {country}, {devices}, {checkCount}, {worstGrade}, {failCount}, {results}, {part}, {partCount}, {message}.

Open a Jira issue per device checked:

{
"url": "https://example.com/",
"modelProvider": "gemini",
"apiKey": "<your-gemini-api-key>",
"country": "US",
"devices": ["desktop", "mobile", "tablet"],
"mcpConnector": "<your-authorized-jira-connector>",
"deliveryMode": "perResult",
"mcpTool": "create_issue",
"mcpArguments": {
"summary": "Visual check {gradeLetter} — {device} — {url}",
"description": "{result}"
}
}

Log every run to a Notion QA page:

{
"url": "https://example.com/",
"modelProvider": "gemini",
"apiKey": "<your-gemini-api-key>",
"country": "US",
"devices": ["desktop", "mobile"],
"mcpConnector": "<your-authorized-notion-connector>",
"deliveryMode": "summary",
"mcpTool": "notion-create-pages",
"mcpArguments": {
"parent": { "page_id": "<your-page-id>" },
"pages": [
{
"properties": { "title": "Visual check — {url} — {worstGrade}" },
"content": "{results}"
}
]
}
}

One Airtable row per device:

{
"url": "https://example.com/",
"modelProvider": "gemini",
"apiKey": "<your-gemini-api-key>",
"country": "US",
"devices": ["desktop", "mobile", "tablet"],
"mcpConnector": "<your-authorized-airtable-connector>",
"deliveryMode": "perResult",
"mcpTool": "create_record",
"mcpArguments": {
"fields": {
"URL": "{url}",
"Country": "{country}",
"Device": "{device}",
"Grade": "{gradeLetter}",
"Findings": "{reason}"
}
}
}

📤 Output Contract & Data Structure

Storage: Apify Dataset — one row per device checked, written at the end of the run. Pagination: limit & offset on the dataset items endpoint. Duplicates: none within a run — each device appears once.

Result record — 5 fields

FieldTypeNullableMeaning
urlstringnoThe URL you submitted, echoed back
countrystringnoThe country profile used for the check
devicestring⚠️ null on the row produced when the AI provider fails before any check runsdesktop, mobile or tablet
gradestringnoA+ (90-100) · A (75-89) · B (60-74) · C (<60) · C · N/Asee the warning below
reasonstringnoWritten findings explaining the grade, or the error that prevented a check

Grade semantics

GradeMeaningWhat to do
A+Fully rendered, all content visible, looks professionalNothing
AGood — minor cosmetic imperfection at mostNothing, or note it
BNoticeable defects: missing images, small layout problemsInvestigate
CEither badly broken or not checkable (HTTP error, timeout)Read reason to tell which — then act
N/AThe page was not assessed — your AI provider was unavailableFix the key/quota and re-run

⚠️ Two things to handle in code.

1. The grade string is not a clean enum. A completed analysis carries its band ("B (60-74)"); a grade assigned because the page errored or the run failed is the bare letter ("C"). Match on the leading letter, e.g. grade.startswith("C"), not grade == "C". The MCP delivery exposes a normalised {gradeLetter} for exactly this reason.

2. C is ambiguous by design. A dead site and an ugly site both land there. reason is what separates "HTTP Error 503" from "hero image failed to load".

Example — a healthy page

{
"url": "https://example.com/",
"country": "US",
"device": "desktop",
"grade": "A+ (90-100)",
"reason": "Page is fully loaded with all content visible. Navigation, hero section and footer render correctly with no missing assets or layout defects."
}

Example — a real defect, mobile only

{
"url": "https://example.com/",
"country": "US",
"device": "mobile",
"grade": "B (60-74)",
"reason": "The hero image failed to load, leaving a blank region at the top of the page. The navigation menu overlaps the headline text at this viewport width."
}

Example — page unreachable (also grade C)

{
"url": "https://example.com/",
"country": "US",
"device": "tablet",
"grade": "C",
"reason": "HTTP Error 503: The page returned an error status."
}

Example — provider unavailable, nothing assessed

{
"url": "https://example.com/",
"country": "US",
"device": null,
"grade": "N/A",
"reason": "AI analysis skipped: ❌ Quota Exceeded: Your Google Gemini API key has insufficient credits (Free Tier limit reached)."
}

Consuming the results safely

def letter(row):
"""Normalise the inconsistent grade string to its bare letter."""
grade = (row.get("grade") or "").strip()
for candidate in ("A+", "A", "B", "C"):
if grade.startswith(candidate):
return candidate
return grade or "?" # "N/A" or empty
assessed = [r for r in items if r["grade"] != "N/A"]
if not assessed:
raise SystemExit("Nothing was assessed — check the run's status message.")
failing = [r for r in assessed if letter(r) in ("B", "C")]
for row in failing:
# A "C" can mean a dead page rather than a bad design — reason says which.
print(f'{row["device"]}: {letter(row)}{row["reason"]}')

What a successful run looks like

{
"status": "SUCCEEDED",
"defaultDatasetId": "<datasetId>",
"stats": { "itemCount": 3 }
}

itemCount equals the number of devices selected — unless the AI provider failed, in which case you get fewer rows (the run stops early) or a single N/A row.

⚠️ SUCCEEDED does not mean the page passed. It means the Actor ran. Read the grades — and read the run's status message, which is set whenever an AI provider issue prevented a real assessment.


▶️ Invocation & Integration

Lifecycle: Discover → Understand → Configure → Execute → Retrieve → Validate. Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.

Apify Console

Open the Actor → Input tab → enter the Target URL → pick an AI Model Provider and paste your API Key → choose a Country and one or more Devices(optional) pick an MCP connector, tool and template → Start → read the grades in the Dataset tab.

Model Context Protocol (MCP)

claude mcp add --transport http apify \
"https://mcp.apify.com?tools=techforce.global/visual-verification-agent"

Python (apify-client)

import os
from apify_client import ApifyClient
client = ApifyClient(os.getenv("APIFY_TOKEN"))
ACTOR = "techforce.global/visual-verification-agent"
run = client.actor(ACTOR).call(run_input={
"url": "https://example.com/",
"modelProvider": "gemini",
"apiKey": os.environ["GEMINI_API_KEY"], # your key, billed by the provider
"country": "US",
"devices": ["desktop", "mobile", "tablet"],
"deliveryMode": "none",
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
# SUCCEEDED means the Actor ran - not that the page passed.
print(f'status: {run["status"]} · message: {run.get("statusMessage")}')
def letter(row):
grade = (row.get("grade") or "").strip()
for candidate in ("A+", "A", "B", "C"):
if grade.startswith(candidate):
return candidate
return grade or "?"
for row in items:
print(f'{str(row["device"]):<8} {letter(row):<3} {row["reason"][:90]}')
failing = [r for r in items if letter(r) in ("B", "C")]
skipped = [r for r in items if r["grade"] == "N/A"]
if skipped:
print("⚠️ Some checks were skipped — the AI provider was unavailable.")
elif failing:
raise SystemExit(f"{len(failing)} device(s) below grade A")

Multi-page or multi-country sweep — one run per combination:

PAGES = ["https://example.com/", "https://example.com/pricing"]
COUNTRIES = ["US", "DE"]
for page in PAGES:
for country in COUNTRIES:
run = client.actor(ACTOR).call(run_input={
"url": page, "country": country,
"modelProvider": "gemini", "apiKey": os.environ["GEMINI_API_KEY"],
"devices": ["desktop", "mobile"], "deliveryMode": "none",
})
rows = client.dataset(run["defaultDatasetId"]).list_items().items
worst = min((letter(r) for r in rows), key="A+ A B C".split().index, default="?")
print(f"{page} [{country}] → worst grade {worst}")

JavaScript / TypeScript (apify-client)

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const ACTOR = 'techforce.global/visual-verification-agent';
const run = await client.actor(ACTOR).call({
url: 'https://example.com/',
modelProvider: 'gemini',
apiKey: process.env.GEMINI_API_KEY,
country: 'US',
devices: ['desktop', 'mobile'],
deliveryMode: 'none',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// Grade strings are inconsistent — match the leading letter.
const letter = (row: any) =>
['A+', 'A', 'B', 'C'].find((c) => (row.grade ?? '').startsWith(c)) ?? row.grade ?? '?';
const failing = items.filter((r) => ['B', 'C'].includes(letter(r)));
const skipped = items.filter((r) => r.grade === 'N/A');
console.log(`${items.length} checked · ${failing.length} failing · ${skipped.length} skipped`);
if (skipped.length) console.warn('AI provider was unavailable — results incomplete.');

cURL — synchronous run (small runs; 300s limit)

curl -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
-d '{"url":"https://example.com/","modelProvider":"gemini","apiKey":"<your-key>","country":"US","devices":["mobile"],"deliveryMode":"none"}' \
'https://api.apify.com/v2/acts/techforce.global~visual-verification-agent/run-sync-get-dataset-items'
# 1. Start execution with a hard spend limit
curl -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
-d '{"url":"https://example.com/","modelProvider":"gemini","apiKey":"<your-key>","country":"US","devices":["desktop","mobile","tablet"],"deliveryMode":"none"}' \
'https://api.apify.com/v2/acts/techforce.global~visual-verification-agent/runs?maxTotalChargeUsd=0.50'
# 2. Poll — read statusMessage, not just status
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/actor-runs/<runId>'
# 3. Retrieve when SUCCEEDED
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'

CI / scheduling

The natural pattern is an Apify schedule (or a post-deploy webhook) per critical page, with deliveryMode: "summary" posting into a QA channel. In CI, fail the pipeline when any device comes back below A — but treat N/A as inconclusive, not as a pass or a failure.


⚠️ Autonomous Agent Error Handling & Resolution Matrix

Error CodeDetection ConditionUnderlying CauseDeterministic Agent Action
AUTH_INVALIDHTTP 401 from ApifyMissing or malformed APIFY_TOKENAbort & Prompt User: request a valid Apify API token.
RATE_LIMITEDHTTP 429 from ApifyApify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointMulti-device runs can exceed the 300s synchronous ceilingReconfigure: async POST /runs + polling.
MISSING_API_KEYRun FAILED: API Key is required for <provider> provider.apiKey emptyModify Input: supply a key for the selected provider.
UNKNOWN_PROVIDERRun FAILED: Unknown provider: <x>modelProvider not gemini or openaiModify Input: use a valid provider value.
AI_KEY_INVALIDSUCCEEDED, one row, grade: "N/A", reason mentions an invalid keyThe key was rejected by the providerPrompt User: supply a valid key, then re-run.
AI_QUOTA_EXCEEDEDSUCCEEDED, grade: "N/A", reason mentions quota or creditsProvider quota or credit exhaustedPrompt User: top up or wait, then re-run.
AI_PROVIDER_MIDRUNSUCCEEDED, fewer rows than devices, one row N/AThe provider failed partway; remaining devices were skipped to protect your quotaRetry once the provider is healthy.
CHECK_SKIPPED_NOT_PASSEDConsumer treats N/A as a passN/A means the page was not assessedFix Logic: treat as inconclusive; re-run.
SUCCESS_NOT_PASSConsumer treats run status as the verdictSUCCEEDED only means the Actor ranFix Logic: read grades and the run's statusMessage.
GRADE_STRING_MISMATCHgrade == "C" matches some rows but not othersGrades are banded ("C (<60)") or bare ("C")Fix Code: match the leading letter.
GRADE_C_AMBIGUOUSA site outage reported as a design defectC covers both bad rendering and unreachable pagesFix Logic: branch on reason.
PAGE_HTTP_ERRORgrade: "C", reason starts HTTP Error <code>The page returned an error statusEscalate: this is an availability incident, not a QA finding.
PAGE_TIMEOUTgrade: "C", reason mentions a timeoutThe page took too long to become checkable (often infinite scroll)Retry; if persistent, the page is too heavy to verify this way.
NO_SCREENSHOTWorkflow expects an imageScreenshots are not retainedFix Expectation: capture separately if you need evidence.
NO_BASELINEUser wants visual regressionEach run is judged on its ownFix Expectation: compare grades across runs instead.
MULTI_URL_UNSUPPORTEDAgent passes a URL listOne URL per runLoop: one run per URL.
MULTI_COUNTRY_UNSUPPORTEDAgent passes a country listOne country per runLoop: one run per country.
AUTH_PAGE_REQUESTEDTarget needs a loginNo authentication supportAbort & Prompt User: public pages only.
WRONG_TOOL_PERFUser wants load time / Core Web VitalsNot measuredRe-route: a performance tool.
WRONG_TOOL_A11YUser wants a WCAG auditNot assessedRe-route: an accessibility tool.
GRADE_VARIANCEThe same page grades differently between runsAI judgement is not deterministicFix Logic: alert on a band drop, not on small movement.
DELIVERY_SKIPPEDLog no tool name was providedmcpConnector set but mcpTool emptyModify Input: set mcpTool; the dataset is already saved.
DELIVERY_TOOL_UNKNOWNLog Tool 'x' is not available on this connectorWrong tool name for that connectorModify Input: pick a name from the Available tools: list in the same log line.
DELIVERY_ARGS_INVALIDConnector rejects the argumentsmcpArguments doesn't match the tool's schemaRead the log: the line Connector [<tool>] expects arguments matching this schema: prints the exact shape.
DELIVERY_EMPTYConnector called, body emptymcpArguments omitted the {message} placeholderModify Input: map {message} inside mcpArguments.
MCP_PROXY_MISSINGLog APIFY_MCP_PROXY_URL is not setRunning locally instead of on the platformReconfigure: apify push and run on the platform.
CHARGE_LIMITRun aborted before all devicesmaxTotalChargeUsd spend threshold reachedPrompt User: confirm a higher spend cap.

Reading run status: SUCCEEDED with an empty dataset is a valid outcome here, not a failure — only a FAILED status is a platform-level problem. Every Actor-side row, quoted message and field condition in the table above comes from this Actor’s own behaviour; the 401/408/429 rows are standard Apify API responses.


🗣️ Natural Language → Actor Mapping

User saysIntentConstructed Actor input
"Is our homepage broken?"Smoke check{"url":"<home>","country":"US","devices":["desktop","mobile","tablet"]}
"Check the new landing page before we launch"Release gateall three devices, fail the pipeline below grade A
"Does the site look right on mobile?"Responsive check{"devices":["mobile"]}
"Our German visitors say the page is blank"Geo check{"country":"DE","devices":["desktop","mobile"]}
"Are we geo-blocked in China?"Geo-restriction{"country":"CN","devices":["desktop"]}
"Did the deploy break anything visually?"Post-deployschedule/webhook run, alert on a grade drop
"Monitor our pricing page daily"MonitoringApify schedule + deliveryMode: "summary" to Slack
"Open a ticket for anything broken"Issue tracking{"mcpConnector":"jira","deliveryMode":"perResult","mcpTool":"create_issue"}
"Which device has the problem?"Diagnosisrun all three; each is graded separately
"Why did it get a C?"Explanationread reason — it distinguishes a bad render from an unreachable page
"Send me the screenshot"Evidence❌ Not retained — the grade and findings are the output
"Compare it to how it looked last week"Visual regression❌ No baseline — compare grades across runs instead
"Check all 40 pages on our site"Crawl❌ One URL per run — loop, or use a crawler for content
"How fast does it load?"Performance❌ Not measured
"Is it accessible?"A11y audit❌ Not assessed
"Check the page after logging in"Authenticated❌ No login support
"Add to cart and check the checkout"User journey❌ Single page load only

Should NOT route here: performance, load time or Core Web Vitals · accessibility / WCAG audits · broken-link, spelling or SEO checks · visual regression against a stored baseline · retrieving the screenshot itself · crawling or checking many URLs in one run · authenticated pages and multi-step user journeys · any run without an OpenAI or Gemini key.


🧭 Agent Execution & Routing Logic

[Input User Query]
1. Is the question "does this page RENDER correctly?" NO → Route elsewhere
│ YES (performance / a11y / SEO / crawling
▼ are all different tools)
2. Does the user need the SCREENSHOT, or a comparison to a baseline?
│ YES → Abort: images are not retained and there is no visual regression baseline
3. Is the page public? NO → Abort: no login support
4. ── PRECONDITION ── Is an OpenAI or Gemini API key available?
│ NO → Abort and say why: the grade comes from a vision model, and the
│ key is a required input billed to the user's own provider account
│ YES → set modelProvider + apiKey
5. One URL and one country per run.
Several pages or markets → LOOP, one run per combination
6. Pick devices — each one is a separate graded row and a separate AI call:
├── quick/cheap check → ["mobile"] (where breakage usually shows first)
└── release gate → ["desktop","mobile","tablet"]
7. Deliver to a connector? YES → mcpConnector + mcpTool + {message}
│ summary (1 call) / perResult (1 per device) / chunked
│ NO → set deliveryMode "none" (it defaults to summary)
[Execute]
8. INTERPRET CAREFULLY — run status is NOT the verdict:
├── any row grade "N/A" → the page was NOT ASSESSED (AI provider issue).
│ Read the run's statusMessage. Treat as INCONCLUSIVE:
│ never count it as a pass. Fix the key/quota, re-run.
├── grade starts "C" → read `reason`:
│ "HTTP Error..." or timeout → AVAILABILITY incident, escalate as an outage
│ anything else → genuine rendering failure, raise a QA ticket
├── grade starts "B" → real defect worth investigating
└── grade starts "A" → healthy
9. Matching grades: use the LEADING LETTER, never string equality —
a graded result is "B (60-74)" but an errored one is bare "C".

💰 Cost Control & Pricing Transparency

Two separate bills — budget for both.

CostWho charges youDriver
Apify: pay-per-resultApifyOne result per device checked. Rate on the Actor's Pricing tab
AI providerOpenAI or Google, directly against your keyOne vision call per device checked
Apify platform usageApifyBrowser time — one page load and capture per device

ℹ️ Always confirm the live rate on the Actor’s Apify Store page before committing to a large run. The Store listing is what bills you; the figures above are a worked example at the advertised rate. Apify platform usage is billed separately.

The multiplier is devices × runs

ConfigurationRowsAI calls
1 device, 1 run11
3 devices, 1 run33
3 devices, daily for a month~90~90
3 devices × 2 countries × 5 pages30 per sweep30 per sweep

Cost levers:

  • Check one device for routine monitoring. Mobile is where breakage usually shows first; save all three for release gates.
  • Don't over-schedule. A page that changes on deploy needs checking on deploy, not hourly.
  • A failed key costs almost nothing. The key is validated before any page is loaded, so a bad key ends the run early instead of burning browser time.
  • Choose your provider on price. Both are supported; the cheaper one is whichever your account already has favourable rates on.
  • Cap the Apify spend with maxTotalChargeUsd on any agent-driven run — note this does not cap your AI provider spend, which is billed outside Apify.

🔍 Companion machine-readable files

FilePurpose
.actor/actor.jsonIdentity, title, description, version, build tag, and the input / output / dataset schema references
.actor/input_schema.jsonAuthoritative typed input contract — the five required fields, the 15-country and 3-device enums, and the 5 delivery fields
.actor/dataset_schema.jsonConsole Output view — device, grade, findings, country, URL
.actor/output_schema.jsonDeclares where results are stored

If any table on this page disagrees with the schema files, the schema files win.


🛠️ Troubleshooting

#Symptom you seeMost likely causeFix
1Run FAILED: API Key is required…apiKey emptySupply a key for the selected provider.
2One row, grade: "N/A", reason mentions an invalid keyThe provider rejected the keyCheck you pasted the key for the provider you selected.
3grade: "N/A", reason mentions quota or creditsProvider quota or credit exhaustedTop up or wait, then re-run.
4Fewer rows than devices selectedThe provider failed partway; remaining devices were skipped to protect your quotaRe-run once the provider is healthy.
5Run says SUCCEEDED but nothing was checkedAI issues complete the run rather than failing itRead the run's status message and the grades.
6grade == "C" matches some rows but not othersGrades are banded ("C (<60)") or bare ("C")Match the leading letter, not the whole string.
7A site outage looks like a design defectC covers bothRead reason — an HTTP error says so explicitly.
8grade: "C", reason mentions a timeoutThe page took too long to become checkable — often infinite scrollRetry; very heavy pages may not be verifiable this way.
9No screenshot anywhereScreenshots are not retainedCapture separately if you need visual evidence.
10Grades move slightly between identical runsAI judgement isn't deterministicAlert on a band drop, not on small movement.
11A grade seems too harsh or too kindSame cause — it's a model's judgementRead reason; if it describes something real, trust it.
12device is null on a rowThat's the row produced when the provider fails before any checkExpected — nothing was assessed.
13Only one country came backOne country per runLoop, one run per country.
14Only one URL was checkedOne URL per runLoop, one run per URL.
15Page behind a login can't be checkedNo authentication supportPublic pages only.
16Content differs from what you expectedcountry genuinely changes what the site servesThat's the feature — check the country you meant.
17HTTP 408 on run-sync-get-dataset-itemsThe synchronous endpoint has a hard 300-second ceilingUse async POST /runs → poll → fetch.
18Connector rejected the argumentsmcpArguments doesn't match the tool's schemaRead the Connector [<tool>] expects arguments matching this schema: log line.
19Slack / Jira received nothingDelivery needs both mcpConnector and mcpToolSet both. The dataset is still written in full.
20Connector fired but the body is emptymcpArguments did not reference {message}Map {message} and populate mcpMessageTemplate.
21Log APIFY_MCP_PROXY_URL is not setYou ran locally; connectors only resolve on the platformDeploy with apify push and run on the platform.
22HTTP 401 / 403 from ApifyMissing, expired or malformed APIFY_TOKENRegenerate in Apify Console → Settings → API & Integrations.
23HTTP 429 from ApifyApify account concurrency / rate limitsRetry with exponential backoff (2s → 4s → 8s).

Known limitations

  • The screenshot behind a grade is not retained — you get the verdict and the written findings, not the image.
  • No visual regression baseline — each run is judged on its own merits, so compare grades across runs rather than pixels.
  • AI judgement is not deterministic. Grades can shift slightly between identical runs; alert on band changes.
  • One URL and one country per run.
  • No authenticated pages, no multi-step journeys, no performance or accessibility measurement.
  • An AI key is mandatory, and its cost is yours.

Diagnostic checklist before opening an issue

  1. Check the run's status message. Any AI provider issue is reported there, and it's the difference between "the page is fine" and "the page was never checked".
  2. Baseline run. A URL you know renders correctly, devices: ["desktop"] — confirms your key and the pipeline in one cheap check.
  3. Grade A+ or A? Then the pipeline works, and the earlier result was about the page, not the Actor.
  4. Read reason on any C. An HTTP error or timeout means the page couldn't be checked — a very different problem from a bad layout.
  5. Open the URL yourself at the same device profile and country. If you see what reason describes, the grade is doing its job.

If the issue survives all five steps, open an Issues ticket on the Actor page (or email support) with the run ID, the exact input JSON (without your API key), and what you expected the grade to be.

❓ FAQ

Setup

Do I need my own AI API key?

Yes — it's required. Choose Google Gemini or OpenAI, paste your key as a secret input, and that provider bills you directly for one vision call per device checked. The key is validated before any page is loaded, so mistakes are cheap.

Which provider should I pick?

Whichever you already have credit and favourable rates on. Both are supported and both produce a grade and written findings in the same output shape.

Is my API key safe?

It's a secret input, stored encrypted by Apify and used only to call the provider you selected. It never appears in the dataset or the run log.

Can I plug it into Claude, Cursor, or a LangChain agent?

Yes — it is a native MCP tool:

claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/visual-verification-agent"

What it checks

What counts as "broken"?

Blank pages, error screens, stuck loading placeholders, missing images, overlapping or collapsed layouts, and content that never appeared. The grade tells you how bad it is; reason tells you what it saw.

Does the country setting really change anything?

Yes — the page is requested as a visitor from that country, so geo-targeted content, languages, redirects and regional blocks show up as they would for that audience. Checking CN or DE can produce genuinely different results from US.

Can it check pages behind a login?

No — public pages only.

Does it measure speed or accessibility?

No. This is a rendering-quality check. Use a dedicated performance tool for Core Web Vitals and a dedicated accessibility tool for WCAG.

Can it check my whole site?

Not in one run — it takes a single URL. Loop it over your key pages from your own orchestration layer.

Interpreting results

What does grade C mean exactly?

Either the page rendered badly, or it couldn't be checked at all — an HTTP error or a timeout. reason tells you which, and the difference matters: one is a QA ticket, the other is an outage.

What does N/A mean?

The page was not assessed — your AI provider was unavailable. It's neither a pass nor a fail. Fix the key or quota and re-run.

Why does grade == "C" miss some rows?

Because a graded result carries its band ("C (<60)") while an errored one is the bare letter ("C"). Match the leading letter instead — the MCP delivery exposes a normalised {gradeLetter} for this.

Why did the run succeed when the page is broken?

SUCCEEDED means the Actor ran, not that the page passed. The verdict is in the grades.

Can I see the screenshot it graded?

No — it isn't retained. You get the grade and the written findings.

Why did the same page get a different grade today?

The grade comes from a vision model, so small variation is normal. For monitoring, alert when a grade drops a band rather than on every point of movement.

Pricing

How is this billed?

Two bills: Apify charges pay-per-result (one result per device), and your AI provider charges you directly for one vision call per device. Apify platform usage (browser time) is billed separately again.

How do I keep it cheap?

Check one device for routine monitoring, all three only for release gates, and don't schedule more often than the page actually changes.

Does maxTotalChargeUsd cap everything?

No — it caps the Apify spend only. Your AI provider spend is outside Apify's control; manage it with quotas on the provider side.


Different questions about the same website

ActorAnswersWhy pick it over this one
This Actor"Does this page render correctly for real visitors?"Graded visual verdict per device and country
Advanced Website Crawling Actor"What content is on this site?"Crawls many pages and extracts their content
Website Contact Scraper"How do I contact this business?"Emails, phones and social links from a site
Google Maps Business Leads & Sales Intelligence"Which local businesses exist, and how good are their sites?"Finds businesses and grades their web presence at scale

ℹ️ Complementary, not overlapping. This Actor verifies one page deeply across devices and geographies; the crawler covers many pages shallowly. Use the crawler to find your key URLs, then verify each one here.

Suggested pipeline patterns

🚦 Release gate Post-deploy webhook → check your top 3 pages on all devices → fail the pipeline if any grade drops below A, and treat N/A as inconclusive rather than a pass.

📉 Rendering monitor Daily schedule per critical page, mobile only → deliveryMode: "summary" into a QA channel → alert when a grade drops a band.

🌍 Geo rollout check Before launching in a new market, run the same URL across the countries you serve → confirm nothing is blocked, mistranslated or redirected wrongly.

🎫 Auto-triaged QA tickets deliveryMode: "perResult" into Jira or Linear with {gradeLetter} in the summary → each failing device becomes its own ticket with the findings attached.

🔎 Crawl, then verify Advanced Website Crawling Actor to enumerate your important URLs → loop this Actor over them → a rendering health report for the whole site.

Browse all Actors by Techforce Global at scraper.techforce.global.


🔐 Compliance & Data Privacy

This Actor loads a single public web page that you specify and assesses how it rendered. It does not log in, does not submit forms, does not crawl beyond the page you give it, and does not extract content from the page into the dataset — the output is a grade and a written assessment.

Four things worth being deliberate about:

  • Point it at pages you own or are authorised to test. It's built for verifying your own properties and those of clients who've engaged you. Running repeated automated checks against a third party's site without permission is a different activity with different risks.
  • Your API key goes to your chosen AI provider. The page's rendered view is sent to OpenAI or Google for assessment under their terms and data-handling policies — review those if the page contains anything sensitive. Nothing is sent to any other third party, and the captured view is discarded after the assessment.
  • Don't verify pages showing personal data. A page rendered while logged in, or one displaying customer records, would transmit that content to the AI provider. This Actor has no login support precisely because it's meant for public pages; keep it that way.
  • A grade is an AI's judgement, not a certification. It's a strong operational signal, and it can be wrong. Don't present it as a compliance attestation, an accessibility conformance claim, or a guarantee about someone else's website.

You remain responsible for your AI provider's terms, for having authorisation to test the target page, and for applicable data-protection law where the page or its content involves personal data.


🆘 Support & Custom Pipeline Engineering

Need multi-URL batch verification, retained screenshots, visual regression against a baseline, custom grading criteria for your design system, or CI integration that gates deploys on rendering health?


Made with ❤️ by Techforce Global Specialists in High-Performance Web Scrapers and AI Automation.


🏷️ Structured data for search & AI discovery

{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Visual Verification Agent — AI Website QA Across Desktop, Mobile & Tablet",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Cloud (Apify platform)",
"description": "An AI visual QA agent that loads a web page on desktop, mobile and tablet as a visitor from any of 15 countries, has a vision model judge whether the page rendered correctly, and returns a letter grade from A+ to C with written findings for each device. Detects blank pages, error screens, stuck loading states, missing images and broken layouts, and distinguishes a badly rendered page from an unreachable one. Bring your own OpenAI or Google Gemini API key. Results can be delivered into Slack, Jira, Linear, Notion, Airtable or any authorized MCP connector. Screenshots are not retained.",
"url": "https://apify.com/techforce.global/visual-verification-agent",
"offers": {
"@type": "Offer",
"priceCurrency": "USD",
"description": "Apify: pay-per-result, one result per device checked — the current rate is listed on the Actor's Pricing tab. AI provider costs are billed separately by OpenAI or Google against your own API key."
}
}
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Do I need my own AI API key?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes - it's required. Choose Google Gemini or OpenAI, paste your key as a secret input, and that provider bills you directly for one vision call per device checked. The key is validated before any page is loaded, so mistakes are cheap."
}
},
{
"@type": "Question",
"name": "Which provider should I pick?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Whichever you already have credit and favourable rates on. Both are supported and both produce a grade and written findings in the same output shape."
}
},
{
"@type": "Question",
"name": "Is my API key safe?",
"acceptedAnswer": {
"@type": "Answer",
"text": "It's a secret input, stored encrypted by Apify and used only to call the provider you selected. It never appears in the dataset or the run log."
}
},
{
"@type": "Question",
"name": "Can I plug it into Claude, Cursor, or a LangChain agent?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes - it is a native MCP tool:"
}
},
{
"@type": "Question",
"name": "What counts as \"broken\"?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Blank pages, error screens, stuck loading placeholders, missing images, overlapping or collapsed layouts, and content that never appeared. The grade tells you how bad it is; reason tells you what it saw."
}
},
{
"@type": "Question",
"name": "Does the country setting really change anything?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes - the page is requested as a visitor from that country, so geo-targeted content, languages, redirects and regional blocks show up as they would for that audience. Checking CN or DE can produce genuinely different results from US."
}
},
{
"@type": "Question",
"name": "Can it check pages behind a login?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No - public pages only."
}
},
{
"@type": "Question",
"name": "Does it measure speed or accessibility?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. This is a rendering-quality check. Use a dedicated performance tool for Core Web Vitals and a dedicated accessibility tool for WCAG."
}
},
{
"@type": "Question",
"name": "Can it check my whole site?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Not in one run - it takes a single URL. Loop it over your key pages from your own orchestration layer."
}
},
{
"@type": "Question",
"name": "What does grade C mean exactly?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Either the page rendered badly, or it couldn't be checked at all - an HTTP error or a timeout. reason tells you which, and the difference matters: one is a QA ticket, the other is an outage."
}
},
{
"@type": "Question",
"name": "What does N/A mean?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The page was not assessed - your AI provider was unavailable. It's neither a pass nor a fail. Fix the key or quota and re-run."
}
},
{
"@type": "Question",
"name": "Why does grade == \"C\" miss some rows?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Because a graded result carries its band (\"C (<60)\") while an errored one is the bare letter (\"C\"). Match the leading letter instead - the MCP delivery exposes a normalised {gradeLetter} for this."
}
},
{
"@type": "Question",
"name": "Why did the run succeed when the page is broken?",
"acceptedAnswer": {
"@type": "Answer",
"text": "SUCCEEDED means the Actor ran, not that the page passed. The verdict is in the grades."
}
},
{
"@type": "Question",
"name": "Can I see the screenshot it graded?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No - it isn't retained. You get the grade and the written findings."
}
},
{
"@type": "Question",
"name": "Why did the same page get a different grade today?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The grade comes from a vision model, so small variation is normal. For monitoring, alert when a grade drops a band rather than on every point of movement."
}
},
{
"@type": "Question",
"name": "How is this billed?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Two bills: Apify charges pay-per-result (one result per device), and your AI provider charges you directly for one vision call per device. Apify platform usage (browser time) is billed separately again."
}
},
{
"@type": "Question",
"name": "How do I keep it cheap?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Check one device for routine monitoring, all three only for release gates, and don't schedule more often than the page actually changes."
}
},
{
"@type": "Question",
"name": "Does maxTotalChargeUsd cap everything?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No - it caps the Apify spend only. Your AI provider spend is outside Apify's control; manage it with quotas on the provider side."
}
}
]
}