whynotAI Lab

Check sale-price JSON-LD after a promotion ends

Опубликовано Oct 6, 20265 мин чтенияСредний
6просмотров
Что понадобится

A promotion ends at 07:00 UTC. Your product data returns to £100, while the page's Product JSON-LD still says £80. A comparison at that boundary can flag the stale structured price for review.

Merchant Sale Transition Audit compares the price expected from your declared feed terms with a static HTML snapshot's JSON-LD. It preserves the evaluation time, HTML hash and JSON-LD location, then compares the result with an earlier baseline. This guide uses an invented product so you can reproduce the behavior without fetching a store.

Disclosure: this is a tutorial for our own Actor, produced with AI assistance and checked against its implementation. The example is synthetic; it is not a customer case study.

What this check covers

Google asks merchants to submit a sale price that agrees with the landing page and checkout, and supports a sale-price date range. See the sale_price requirements for the full rules.

This Actor checks a narrower part of that workflow: declared feed prices against static Product/Offer JSON-LD. You supply the SKU, currency, tax basis and promotion timestamps. It does not import a Merchant Center account or infer your tax treatment.

Its interval convention is explicit: the sale starts inclusively and ends exclusively. At the end timestamp, the expected price returns to the regular price. Both timestamps must include a timezone offset. This is the Actor's comparison rule, not a claim to reproduce every Merchant Center timing behavior.

Run a small example in Apify

Open the Actor page and choose its run option. In the input editor, switch to JSON and paste this input. Check the current platform and Actor pricing shown before starting a run.

json
{
  "feed": [
    {
      "sku": "DEMO-1",
      "price": "100",
      "currency": "GBP",
      "price_basis": "tax_inclusive",
      "sale_price": "80",
      "sale_start": "2026-10-06T06:00:00+00:00",
      "sale_end": "2026-10-06T07:00:00+00:00",
      "url": "https://shop.example/product/demo-1",
      "html": "\u003cscript type=\"application/ld+json\"\u003e{\"@type\":\"Product\",\"sku\":\"DEMO-1\",\"offers\":{\"@type\":\"Offer\",\"price\":\"80\",\"priceCurrency\":\"GBP\"}}\u003c/script\u003e",
      "evidence_label": "synthetic_tutorial"
    }
  ],
  "mode": "simulation",
  "checkedAt": "2026-10-06T07:00:00Z",
  "fetchPublicUrls": false
}

The \u003c and \u003e sequences are JSON escapes for angle brackets. After JSON parsing, the html field contains an ordinary JSON-LD script element.

Start the run and open its dataset results. With this input, the expected row is:

FieldValueMeaning
skuDEMO-1The exact Product SKU
expected100Regular price at the exclusive sale end
observed80Price still present in supplied JSON-LD
statusmismatchThe declared static prices differ
classificationunbaselined_issueA difference with no earlier baseline
evaluation_modesimulationA hypothetical evaluation, not a live observation
pointer0:/offersScript index and JSON location

Change checkedAt to 2026-10-06T06:59:59Z and run again: the expected price is £80, so the row becomes clear. Both runs evaluate the same invented markup. They do not prove that a real page changed over time.

Apify explains how Actor runs and results work. This Actor also writes SUMMARY, BASELINE and audit.csv to its run's key-value store.

Tell a new issue from an old one

To reproduce a transition comparison, run the example at 06:59:59 UTC first. Copy the complete array from its BASELINE record. Return to the input, add that array as baseline, and restore checkedAt to 07:00:00 UTC. The stale £80 offer is now classified as new_issue relative to that earlier compatible simulation baseline.

Use the complete output records; a hand-written list of SKU and price is not a valid baseline. When the same difference persists, a compatible earlier baseline can produce existing_issue; when an earlier difference now matches, it can produce resolved. Changed product identity, feed terms or non-earlier baseline times can make a comparison incomparable.

For a real promotion release, capture fresh, authorized HTML before and after the boundary. Use mode: "observed", provide each snapshot's timezone-aware captured_at, and carry the earlier run's complete BASELINE into the later run. Omit checkedAt, or make it exactly equal the capture time. Processing an old snapshot later does not turn it into a fresh observation.

Handle incomplete evidence

Treat needs_review as an unresolved question. Missing SKU, multiple matching Offers, malformed JSON-LD, different currency and ambiguous tax evidence can prevent a price verdict. A declared variant currently matches the exact Product size value; unsupported variant identity needs review.

For owned pages with suitable public static HTML, you can enable fetchPublicUrls. Fetching is bounded to three page attempts and six HTTP requests in total, including robots checks. Redirects, browser rendering, cookies and private-network URLs are unsupported. Supplied snapshots let you inspect authorized HTML without depending on live fetch availability.

A clear row means the declared static fields agree in the stated evidence mode. Visible prices, checkout, shipping, geography, stock, customer discounts, JavaScript rendering and Google approval remain outside this check. Keep those checks in your release process, and investigate the Actor's evidence before changing product data.

Open Merchant Sale Transition Audit on Apify to try the synthetic input, then replace it with a small sample you are authorized to audit.

Белая ракета с лаймовым огнём

Научись использовать Claude и Codex в своих проектах и бизнесе и стань в 10 раз эффективнее

Без опыта и без кода. Первого агента соберёшь за вечер, дальше он работает на тебя: клиенты, продажи, рутина.

Было полезно?

Похожие гайды