Agentic Commerce Readiness Index - UCP, ACP & AI-Shopping Audit avatar

Agentic Commerce Readiness Index - UCP, ACP & AI-Shopping Audit

Pricing

from $0.00005 / actor start

Go to Apify Store
Agentic Commerce Readiness Index - UCP, ACP & AI-Shopping Audit

Agentic Commerce Readiness Index - UCP, ACP & AI-Shopping Audit

Score any storefront 0-100 on how ready it is for AI shopping agents — Universal Commerce Protocol manifest, ACP product-feed conformance, structured data, AI-crawler policy — and track adoption over time. Cohort mode turns 500 merchants into an adoption curve you cannot buy retroactively.

Pricing

from $0.00005 / actor start

Rating

0.0

(0)

Developer

Eonix Pvt Ltd

Eonix Pvt Ltd

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

19 days ago

Last modified

Share

Agentic Commerce Readiness Index — UCP, ACP & AI-Shopping Audit

The adoption curve you cannot buy retroactively.

The Universal Commerce Protocol went self-serve on Shopify in June 2026. Right now, every week, merchants are switching it on. Nobody is writing that down.

This actor scores any storefront 0–100 on how ready it is for AI shopping agents — and, because it snapshots every scan, it turns a merchant list into a longitudinal dataset of protocol adoption. Run it once and you get an audit. Run it weekly against 500 merchants and you get something that cannot be reconstructed after the fact at any price: who adopted, when, in what order, and what they turned on first.

A competitor can clone the audit in an afternoon. Nobody can clone six months of history.


The lead example: audit 500 merchants, show me who switched UCP on this month

{
"domains": ["merchant1.com", "merchant2.com", "…498 more"],
"productSampleSize": 25,
"generateReports": false
}

Put that on a weekly Schedule. Every run writes COHORT.json and emits a typed change record for every merchant whose posture moved:

ucp_adopted gymshark.com switched UCP on at version 2026-04-08.
capability_added kith.com added the UCP capability dev.ucp.shopping.checkout.
payment_handler_changed mejuri.com changed its UCP payment handlers.
ucp_version_changed skims.com moved from UCP 2026-01-23 to 2026-04-08.

Read CHANGES.json and filter changeType to ucp_adopted. That is your answer, and it gets more valuable every week you keep the schedule running.

Change events are in the dataset too, as kind: "change" rows. Reach for CHANGES.json when you want the changes on their own: an Apify dataset view selects columns without filtering rows, so the dataset's "Adoption changes" view still returns every audit row with the change columns blank.

Auditing your own single store is the demo, not the product. A per-store UCP validator already exists and does that job well. What this actor sells is the cohort view and the adoption deltas.


What you get

OutputWhereWhat it is
Per-domain auditdataset, kind: "record"Full readiness profile: score, six sub-scores, raw UCP manifest, ACP feed gaps, crawler matrix
Adoption changesdataset, kind: "change"Typed events since the previous scan of that domain
Product feed gapsdataset, kind: "record" with productIdPer-product missing-field list (opt in with includeProductDetail)
Run summarydataset, kind: "summary"Totals, artifact URLs
CHANGES.jsonkey-value storeThe change feed alone — the only output that is just changes, with counts by type
COHORT.jsonkey-value storeAdoption rates by capability, payment handler, platform and version; median score; leaderboard
INDEX.jsonkey-value storeEvery domain, sortable
REPORT-<domain>.mdkey-value storeHuman-readable audit (opt in — see pricing)
SUMMARY.jsonkey-value storeSame as the summary record
COST-ESTIMATE.jsonkey-value storeWhat this run will cost you, written before work begins

Real sample output

Copy-pasted from a real local run against allbirds.com on 16 August 2026. Trimmed only where marked.

{
"kind": "record",
"target": "allbirds.com",
"status": "ok",
"collectedAt": "2026-08-16T18:21:15.116Z",
"source": "agentic-commerce-radar",
"score": 89.8,
"grade": "A",
"ucpPresent": true,
"ucpVersion": "2026-04-08",
"platform": "shopify",
"acpReadyPct": 100,
"subScores": {
"ucpManifest": 30,
"ucpCapabilityDepth": 10,
"acpFeedConformance": 23.8,
"structuredData": 16,
"agentCrawlerPolicy": 10,
"agentCard": 0
},
"maxScores": {
"ucpManifest": 30,
"ucpCapabilityDepth": 10,
"acpFeedConformance": 25,
"structuredData": 20,
"agentCrawlerPolicy": 10,
"agentCard": 5
},
"topFixes": [
{
"area": "agentCard",
"pointsAvailable": 5,
"action": "Publish an agent card at /.well-known/agent-card.json describing your agent-facing endpoints. Optional today, and a cheap leading signal."
},
{
"area": "structuredData",
"pointsAvailable": 4,
"action": "Add JSON-LD Organization markup to product pages — this is how agents read a store that has no UCP manifest."
}
],
"ucp": {
"present": true,
"httpStatus": 200,
"version": "2026-04-08",
"supportedVersions": ["2026-04-08", "2026-01-23"],
"capabilities": [
"dev.ucp.shopping.checkout",
"dev.ucp.shopping.fulfillment",
"dev.ucp.shopping.discount",
"dev.ucp.shopping.cart",
"dev.ucp.shopping.order",
"dev.ucp.shopping.catalog.search",
"dev.ucp.shopping.catalog.lookup",
"dev.shopify.catalog"
],
"transports": ["mcp", "embedded"],
"paymentHandlers": ["com.google.pay", "dev.shopify.card", "dev.shopify.shop_pay"],
"services": [
{
"serviceId": "dev.ucp.shopping",
"transport": "mcp",
"endpoint": "https://weareallbirds.myshopify.com/api/ucp/mcp"
},
{ "serviceId": "dev.ucp.shopping", "transport": "embedded", "endpoint": null }
],
"drift": false,
"rawManifest": "<full manifest stored verbatim — trimmed here>"
},
"crawlerPolicy": {
"robotsPresent": true,
"allowedCount": 11,
"blockedCount": 0,
"payPerCrawl": false,
"policyHash": "882049824c94f373"
},
"feed": {
"surface": "shopify-products-json",
"productsSampled": 25,
"acpReadyCount": 25,
"acpReadyPct": 100,
"fieldGapCounts": {},
"seller": {
"missingFields": ["seller_name"],
"truncationWarnings": [],
"sellerName": null,
"sellerUrl": "https://allbirds.com"
}
},
"structuredData": {
"homepageBlocks": 0,
"productBlocks": 5,
"types": ["ProductGroup"],
"hasProduct": true,
"hasOffer": true,
"hasAggregateRating": true,
"productCompleteness": 1
},
"warnings": [],
"durationMs": 2118
}

Scoring — published weights, always with sub-scores

The total is never given without the parts. If you disagree with the weighting, recompute your own total from subScores without re-running the audit — that is why they are first-class output.

AreaWeightWhat earns it
ucpManifest30A live, parseable manifest at /.well-known/ucp. Most of the weight is presence; the rest rewards a declared version, multiple supported versions, and a service with a reachable endpoint.
ucpCapabilityDepth10How much of a purchase can actually complete — coverage of catalog search/lookup, cart, checkout, fulfillment, discount, order — plus declared payment handlers.
acpFeedConformance2520 for the share of sampled products carrying every required ACP field, 5 for seller-level fields.
structuredData20JSON-LD Product/Offer/Organization/AggregateRating presence, weighted toward how complete the richest Product/Offer actually is.
agentCrawlerPolicy10Share of tracked AI crawlers permitted by robots.txt.
agentCard5A published agent-card.json or ai-plugin.json.

Grades: A ≥ 85 · B ≥ 70 · C ≥ 55 · D ≥ 35 · F < 35.

Two deliberate calls worth knowing about:

  • A pay-per-crawl posture (HTTP 402) is not scored as a block. Charging agents for access is a legitimate commercial stance, so it costs a small deduction for friction rather than the full ten points.
  • productSampleSize: 0 redistributes its 25 points across the other areas instead of capping every store at 75. maxScores always tells you what was actually attainable. A store whose catalog genuinely cannot be read scores 0 there — an agent cannot read it either.

Robots.txt is a scoring input, not a product

The AI-crawler matrix lives inside the score object and nowhere else. This actor deliberately does not emit a standalone crawler-policy feed — that overlaps a site-health auditor and a dedicated crawler panel.


Free plan limits

Unmonetized runs are capped to 1 target and 5 records. Paid plans run uncapped. Report generation is a paid feature. The run still produces a complete, real audit for that one domain, and the log says plainly what was capped.

"Unmonetized" means pay-per-event pricing is not active on the run — a local apify run, or an Actor with no pricing configured. Once pay-per-event pricing is live, runs are uncapped and bounded by the platform's own total-charge ceiling instead. Verify either side locally:

$ACTOR_TEST_PAY_PER_EVENT=true npx tsx scripts/check-gating.ts

What a run costs you

Prices are per event. The actor writes COST-ESTIMATE.json and logs a table before any work begins, so you never discover the bill afterwards.

EventPriceFires
domain-audited$0.02Once per domain, after its record is pushed
manifest-parsed$0.03Only when a UCP manifest was found and parsed
product-validated$0.02Per 100 products validated, rounded up, once per run
store-report$2.00Per REPORT.md written — off by default
adoption-change$0.01Per change event
request-served$0.001Standby requests only

Worked example — the shipped default (3 domains, 25 products each, no reports):

domain-audited 3 × $0.02 = $0.06
manifest-parsed 2 × $0.03 = $0.06 (store.google.com has no manifest, so it is not charged)
product-validated 1 × $0.02 = $0.02 (50 products → 1 charge unit)
adoption-change 0 × $0.01 = $0.00 (first scan of each domain is a baseline)
------
$0.14

A 500-merchant cohort sweep costs roughly 500 × $0.02 + (adopters × $0.03) + ~$2.50 of product validation ≈ $13–$28 per run depending on adoption rate. Weekly, that is a dataset for the price of lunch.

Reports are the one expensive event. At $2.00 each, a 500-domain sweep with generateReports on bills over $1,000. It ships off for exactly that reason. Turn it on for the handful of merchants you want a document for, not for a sweep.

Things you are never charged for: unreachable domains, domains with no UCP manifest (no manifest-parsed), the first scan of a domain (no adoption-change), failed report writes, or failed standby requests.


Pricing calibration — measured, not assumed

The prices above started as category-median hypotheses. They were validated against real platform cost before publishing, because profit = (0.8 × revenue) − platform cost and a mispriced actor simply runs at a loss.

Every run writes UNIT-ECONOMICS.json and logs a verdict. On the platform the harness reads usageUsd back off the run object (mode: "platform-actual"); locally it estimates from your configured rates.

Platform-measured on 16 August 2026 — the authoritative figures. Both are real runs in mode: "platform-actual", differing only in allocated memory:

MemoryCompute unitsPlatform cost, 1 domainFloor (3×)Headroom on $0.02
4096 MB (as created)0.002944$0.001712$0.006423.1x
1024 MB (correct)0.000658$0.000373$0.0014014.3x

The first platform run overturned a conclusion. Local estimates had bytes dominating cost. On the platform the actor was allocated the full 4096 MB and compute swamped everything — 99.8% of platform cost. A plain-HTTP actor has no use for 4 GB: dropping to 1024 MB cuts cost 4.6x, measured, with no change in output.

.actor/actor.json declares defaultRunOptions.memoryMbytes: 1024, but Apify applies that only when the actor is first createdapify push will not change it on an actor that already exists. On an already-deployed actor this is a one-time Console change; see "Set the memory default" in RUNBOOK.md. Until it is done, every run costs 4.6x more than it needs to.

Pricing was never at risk either way: even at the wasteful 4096 MB, domain-audited at $0.02 clears its cost-covering floor by 3.1x.

Local scale comparison, with COST_CU_USD=0.25 and COST_TRANSFER_GB_USD=0.20:

1× (3 domains)10× (30 domains)
Platform cost$0.002688$0.017527
Cost per domain$0.000896$0.000584falls 35% with volume ✓
Margin97.60%97.91%HEALTHY

The cost driver was corrected by measurement. The initial hypothesis was product-validated — catalog paging looked like the thing that grows. Running at both scales said otherwise: cost per domain falls with volume while cost per product rises. Every domain costs the same ~12 probe requests regardless of productSampleSize, while a 250-product sample is one extra catalog page. The domain is the unit of work; the product is not. costDriverEvent is domain-audited.

EventHypothesisMeasured floor (3× cost)Verdict
domain-audited$0.02$0.0022 – $0.00346–9× headroom. Correctly priced, arguably generous.
product-validated$0.02$0.0027 – $0.01111.8–7.4× headroom. Correct, but the thinnest line — it tightens as cohorts skew toward reachable Shopify stores, because catalog paging then rises from 29% to 55% of bytes.
manifest-parsed$0.03negligible marginal costPriced on value, not cost: it fires only on the ~43% of domains that carry the signal buyers want.
store-report$2.00negligible marginal costPriced on value. See the warning above about sweeps.
adoption-change$0.01negligible marginal costPriced on value; the diff is pure computation over stored state.

No hypothesis price was found to be wrong. All five sit above their cost-covering floor. The one to watch is product-validated.

The pay-per-event + usage toggle is deliberately NOT enabled. Apify's own docs warn it reduces pricing transparency and hurts quality score. At 97% margin there is no case for it.

Calibrating on your own account

The compute and transfer rates are not hardcoded — they vary by plan and change over time. Read yours off your Apify billing page and set them in .env:

COST_CU_USD=0.25
COST_TRANSFER_GB_USD=0.20
COST_PROXY_GB_USD=8.0

Leave them unset and the harness reports CALIBRATION NOT PERFORMED — rates unset rather than a fake healthy margin.


Reliability

Measured across a 30-domain cohort of real storefronts on 16 August 2026:

  • 30/30 domains produced a usable audit — 100%. 26 clean, 4 partial (a probe degraded, the audit still completed).
  • 13/30 served a live UCP manifest (43.3%). The remaining 17 returned 404 — recorded as signal, not as failure.
  • 1 endpoint-drift detection. marucci.com answered 200 with an HTML body at /.well-known/ucp; it was logged as SOURCE DRIFT and emitted with drift: true rather than being silently counted as an adopter.
  • Default run: 3 domains in ~4 seconds. 30-domain cohort: ~30 seconds.

That last point is the one that matters for the failure mode this market is full of. A well-known competitor reports SUCCESS on 57,620 of 57,636 runs while returning unusable data. A 2xx with the wrong shape is reported here, not swallowed.


Maintenance

UCP moved from 2026-01-23 to 2026-04-08 inside a single quarter. This actor is built for that: the manifest parser never matches against a fixed schema, stores rawManifest verbatim so a later re-parse can recover fields today's parser ignores, and logs UCP schema drift: unknown key <k> instead of throwing.

Commitment: broken-source issues are triaged within 2 business days. Open an issue on the actor's Apify page with the domain and the run ID. Structural upstream breakage (a well-known path moves, a platform changes its catalog API) is prioritised over feature requests.


What this actor does NOT do

Being straight about the edges, because a surprised buyer leaves a one-star review.

  • It does not transact. No orders, no carts, no checkout. It reads manifests; it never exercises them.
  • It requires no merchant credentials, and cannot use them. Every probe is a public, unauthenticated GET against a CORS-open endpoint. It audits stores you do not own — that is the point.
  • It does not render JavaScript. Plain HTTP only, deliberately: that is what makes 500 domains a week affordable. A store whose product data exists only in client-rendered JS will under-score on structured data.
  • It cannot read a private or authenticated catalog. Merchants who gate products.json score 0 on ACP feed conformance, because an AI agent hits the same wall.
  • Seller-level ACP fields are inferred, not authoritative. seller_name comes from homepage JSON-LD Organization and the policy URLs are probed at conventional paths (/policies/privacy-policy on Shopify). A merchant using non-standard paths will show a false gap.
  • It samples, it does not exhaust. ACP conformance comes from up to 250 products and structured data from 3 product pages. Sub-5-point score movements are treated as sampling noise and do not emit a change event.
  • It does not verify that a declared capability works. A manifest claiming checkout is scored as claiming it. Nobody can verify that without transacting, and this actor does not transact.
  • robots.txt posture is a scoring input only. There is no standalone crawler-policy output.
  • Change detection needs a prior scan. The first run against any domain is a baseline and emits nothing. The dataset starts compounding on run two.

Use from an AI agent

Apify MCP

The actor is exposed through the Apify MCP server. Point your MCP client at it and the actor becomes a callable tool:

{
"mcpServers": {
"apify": {
"command": "npx",
"args": ["-y", "@apify/actors-mcp-server", "--actors", "your-username/agentic-commerce-radar"],
"env": { "APIFY_TOKEN": "<your token>" }
}
}
}

Then ask in plain language: "Audit these twelve merchants for UCP support and tell me which ones can complete a checkout."

Standby HTTP

With Standby enabled the actor stays warm and answers GETs directly — same pipeline, same output envelope, same charges, plus request-served at $0.001 so idle compute never runs at a loss.

GET https://<your-actor>.apify.actor/?domains=allbirds.com,gymshark.com&productSampleSize=25
GET https://<your-actor>.apify.actor/health

Query parameters mirror the input schema. Standby caps a request at 25 domains and 50 products, and ignores secret fields entirely — an API key or webhook URL in a query string ends up in access logs, proxies and browser history. Use a normal run for those.


Weekly Schedule recipe

The compounding dataset is the whole point, and it only compounds if the schedule runs.

  1. Actor → SchedulesCreate new schedule
  2. Cron: 0 6 * * 1 (Mondays, 06:00 UTC)
  3. Input: your merchant list, productSampleSize: 25, generateReports: false
  4. Leave storeName at agentic-commerce-snapshots so every run diffs against the same history
  5. Optional: set slackWebhookUrl to get pinged only when something actually moves

Week one is a baseline with no change events. From week two onward, every ucp_adopted record is a data point nobody else has.


Input reference

FieldTypeDefaultNotes
domainsstring[]3 verified domainsBare domains or URLs; normalised to an origin
productSampleSizeinteger250–250. 0 skips product validation and reweights the score
includeProductDetailbooleanfalseOne dataset row per sampled product
generateReportsbooleanfalse$2.00 per report — see pricing
storeNamestringagentic-commerce-snapshotsNamed KV store holding scan history
proxyConfigurationproxynoneOptional; helps when sweeping many domains behind one CDN
slackWebhookUrlsecretPosts only when changes are detected
alertWebhookUrlsecretPOSTs the full JSON change payload
openaiApiKeysecretAdds a plain-English narrative to reports. Nothing else uses it

The shipped defaults — allbirds.com, barnesandnoble.com, store.google.com — were verified live during the build. The first two serve real UCP manifests; the third returns 404. Google co-authored UCP and its own store does not serve one, which is a genuinely useful thing to be able to prove.


Run it locally

npm install
npm run build
npm start

Test, lint, typecheck:

$npm test && npm run lint && npm run typecheck

Exercise the charging path and the economics harness without a live account:

$npm run acceptance

Compare unit economics at 10× scale:

$npm run acceptance:scaled

Configuration lives in .env — copy .env.example and read the comments. Nothing is required to run; the rate variables only affect calibration accuracy.


Operational docs

  • SECURITY.md — security model, SSRF posture, reporting
  • RUNBOOK.md — deploy, roll back, rotate secrets, investigate failures
  • CHANGELOG.md

License

MIT.