# UCP Commerce Profile Monitor - Capabilities & Changes (`maydit/ucp-commerce-profile-monitor`) Actor

Monitor merchant UCP profiles: declared commerce capabilities, protocol versions, service endpoints and saved changes. Read-only discovery; no checkout calls or API keys.

- **URL**: https://apify.com/maydit/ucp-commerce-profile-monitor.md
- **Developed by:** [Brandt May](https://apify.com/maydit) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 merchant profile observations

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

![Track merchant commerce declarations — illustrative UCP profile comparison](https://api.apify.com/v2/key-value-stores/nemaYfVcrTgloz8Wn/records/ucp-commerce-profile-monitor-5c7f145c7ef8.png?signature=MaUhhRamlV1avOssuLFF)

### Track merchant commerce declarations

Keep an inventory of the UCP interfaces your merchant or partner watchlist advertises. Collect public protocol versions, capability names and service endpoints, then compare saved observations before updating an integration plan or client report.

**One result is one merchant-origin observation.** The Actor reads the public `/.well-known/ucp` discovery profile. It reports declarations, not certification, compatibility testing or successful checkout, and never invokes advertised services.

[Open in Console](https://console.apify.com/actors/afSnE6c142L5cKJn9/input) · [First run](#try-one-merchant) · [Output preview](#output-preview) · [Saved comparisons](#save-a-baseline-then-compare) · [Pricing](#pricing)

### Try one merchant

Copy this input into Console's JSON editor for a single-domain check. No API key, login or proxy subscription is required.

```json
{
  "merchantUrls": [
    "https://www.brooklinen.com"
  ],
  "maxResults": 1
}
```

Open **Saved observations** for the row and **Readable run report** for the overview. **Coverage and stop reasons** opens SUMMARY. A parsed HTTP 200 profile is `present`; an actual HTTP 404/410 is a billable `absent` observation. Blocked requests, network failures and malformed payloads produce errors without paid result rows.

Replace the sample with a public merchant root. Bare domains are treated as HTTPS; an exact discovery URL is also accepted. Product pages, query strings, authenticated URLs, localhost and private networks are rejected.

### Output preview

| Integration review | Fields | Meaning |
| --- | --- | --- |
| Merchant and discovery path | `merchantUrl`, `profileUrl`, `finalProfileUrl` | Normalized requested origin, requested profile and final allowed destination |
| Profile presence | `profileStatus`, `httpStatus` | `present` for a parsed 200 profile; `absent` only for an actual 404/410 |
| Advertised version | `protocolVersion`, `supportedVersions` | Current protocol and older version labels declared by the profile |
| Declared services | `services` | Names, versions, transports and public endpoints |
| Declared capabilities | `capabilities` | Names, versions, spec/schema references and extension relationships |
| Handler and key identifiers | `paymentHandlers`, `signingKeyIds` | Handler names, versions and optional IDs; key IDs when published |
| Saved changes | `comparisonStatus`, `changedFields`, `previous`, `previousObservedAt` | Field differences and prior observation |
| Observation time | `observedAt` | Retrieval time, not a merchant deployment timestamp |

Protocol version is omitted for absence; signing-key IDs are omitted when not published. JSON preserves nested declarations; CSV supports a summary inventory. Payment configuration, capability configuration and public-key material are omitted. A key-material change that keeps the same ID is outside the comparison.

### Review three retail profiles

```json
{
  "merchantUrls": [
    "https://www.pier1.com",
    "https://www.allbirds.com",
    "https://www.brooklinen.com"
  ],
  "maxResults": 3
}
```

Each successfully observed origin can produce one row. Empty input or an empty merchant list uses these three demo merchants. Explicit URLs replace them. A result cap is an upper bound, not a promise of that many successful observations.

### Save a baseline, then compare

```json
{
  "merchantUrls": [
    "https://www.pier1.com",
    "https://www.allbirds.com"
  ],
  "maxResults": 2,
  "comparePrevious": true,
  "snapshotName": "retail-capabilities"
}
```

1. Run once to save `new` observations. Review their declaration fields and source coverage.
2. Repeat with the same `snapshotName`. Rows become `changed` or `unchanged`, carrying `previous` values and `previousObservedAt`.
3. Add `onlyChanges: true` to skip unchanged observations. It also enables comparison. A healthy repeat can save zero rows and leaves the previous observation timestamp unchanged.
4. For a client inventory in Sheets, Make or n8n, inspect the completed run's SUMMARY and dataset, then upsert by `merchantUrl`. Retain the run ID to replay delivery and deduplicate by run ID plus merchant origin.

History is scoped per normalized origin in `maydit-ucp-profile-<snapshotName>`. Failed requests, rejected payloads, failed dataset writes and zero-row spending stops preserve the baseline. Successfully saved observations can advance independently even when another merchant fails.

Run the same snapshot name **sequentially**. A precommit check detects an already changed snapshot; it is not an atomic compare-and-swap lock. Check `historyUpdated`, `failedSources` and `baselineSkippedOnDeadline` before relying on the baseline. Ordering alone and observation timestamps do not create changes. You configure schedules and downstream connections.

### Input guide

| Input | Purpose | Default |
| --- | --- | --- |
| `merchantUrls` | Up to 100 public roots, deduplicated by origin | Three example merchants |
| `maxResults` | Saved observations, from 1 to 100 | 25 |
| `comparePrevious` | Enable named history | false |
| `onlyChanges` | Skip unchanged rows; enables comparison | false |
| `snapshotName` | 1–40 letters, digits, underscores or hyphens; starts with a letter or digit | default |
| `maxRunSeconds` | Own budget, from 30 to 3,600 seconds | 240 |

The Actor reserves 25 seconds before the platform deadline. Set an Apify maximum total charge alongside the result cap.

### Coverage and interpretation

- **Discovery only:** fetches robots.txt and the fixed profile path. No product crawl, MCP execution, cart creation, negotiation, linked-schema fetch or payment test. It does not inspect llms.txt, ACP or search visibility.
- **Declared capability:** advertised versions and endpoints do not prove authentication, compatibility, payment acceptance or runtime availability for your application.
- **Observed absence:** HTTP 404/410 describes the requested path at the time checked. It does not prove a merchant lacks all AI commerce integrations. There are no product-removal or historical-archive claims.
- **Supported shapes:** current array declarations and earlier service transport maps are supported. Unknown or malformed structures fail explicitly. This is a metadata parser, not a complete UCP schema validator.
- **References and redirects:** endpoints remain metadata and are never called. References must be public HTTPS URLs without credentials or query parameters. Up to three redirects may lead to another exact public HTTPS profile path. `finalProfileUrl` exposes the destination; following a redirect does not certify UCP's no-redirect hosting requirement.
- **Bounded requests:** sequential domains; profiles limited to 1 MiB. 429/5xx responses receive at most two retries within the deadline, respecting Retry-After. Robots exclusions and public-address checks apply to requests and redirects.

Intentional result caps can finish with status `ok`, with unvisited merchants reported in SUMMARY. Source failures, time exhaustion and spending limits set incomplete coverage and stop flags. An all-failed run fails. Valid unchanged-only output can be empty; consult SUMMARY before drawing a conclusion from zero rows.

### Pricing

Base result rate on the FREE tier: **$5 per 1,000 saved domain observations**, plus the listed **Actor Start** charge. The [Pricing tab](https://apify.com/maydit/ucp-commerce-profile-monitor/pricing) is authoritative for your plan's current rates. Confirmed 404/410 absence and saved unchanged rows are billable. Changes-only skips and error summaries create no dataset-item charge; the start charge still applies. Platform usage charges may apply under your plan.

### Troubleshooting and sources

If a merchant's public page works but the Actor fails, its profile may be blocked, malformed, oversized or unsupported. Restrictions are reported without bypass. For malformed or overlapping history, choose a new snapshot name and keep named workflows sequential.

[Official UCP overview](https://ucp.dev/specification/overview/) · [Shopify developer announcement](https://www.shopify.com/news/spring-26-edition-dev)

This independent tool is not endorsed by UCP, Shopify or the example merchants. Open **Issues** with the public merchant URL, run ID and expected field. Do not include credentials, customer information or checkout details.

# Actor input Schema

## `merchantUrls` (type: `array`):

Up to 100 public HTTPS merchant roots or exact /.well-known/ucp URLs. Paths to products, checkout URLs and query parameters are not accepted. Empty input uses Pier 1, Allbirds and Brooklinen.

## `maxResults` (type: `integer`):

Caps saved domain observations. With changes-only enabled, unchanged domains do not use the saved-row limit.

## `comparePrevious` (type: `boolean`):

Compare each merchant with its previous successfully saved observation. The first observation is marked new. Use one snapshot name sequentially.

## `onlyChanges` (type: `boolean`):

Automatically enables comparison. An unchanged repeat can save zero rows successfully.

## `snapshotName` (type: `string`):

1–40 letters, digits, underscores or hyphens. Use a different name for independent watchlists. Do not run the same name concurrently.

## `maxRunSeconds` (type: `integer`):

Own time budget. The Actor also reserves 25 seconds before the platform deadline for cleanup and reporting.

## Actor input object example

```json
{
  "merchantUrls": [
    "https://www.pier1.com",
    "https://www.allbirds.com",
    "https://www.brooklinen.com"
  ],
  "maxResults": 25,
  "comparePrevious": false,
  "onlyChanges": false,
  "snapshotName": "default",
  "maxRunSeconds": 240
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `report` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "merchantUrls": [
        "https://www.pier1.com",
        "https://www.allbirds.com",
        "https://www.brooklinen.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maydit/ucp-commerce-profile-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "merchantUrls": [
        "https://www.pier1.com",
        "https://www.allbirds.com",
        "https://www.brooklinen.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("maydit/ucp-commerce-profile-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "merchantUrls": [
    "https://www.pier1.com",
    "https://www.allbirds.com",
    "https://www.brooklinen.com"
  ]
}' |
apify call maydit/ucp-commerce-profile-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maydit/ucp-commerce-profile-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/afSnE6c142L5cKJn9/builds/XqiJwuV93Gjk62nir/openapi.json
