Your Product schema is invalid and you don't know it — the price string gotcha

작성자

카테고리:

← 피드로
DEV Community · Daniel Cheong · 2026-07-24 개발(SW)

If you’ve added Product structured data, watched it pass a JSON linter, and still got “Invalid” in Search Console (or no price shown in rich results), you’ve almost certainly hit the one that catches everyone: the price field.

Valid JSON is not valid schema.org. Here’s the trap and the fix.

The markup that looks right but isn’t

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Aeron Chair",
  "offers": {
    "@type": "Offer",
    "price": "$1,299.00",
    "priceCurrency": "USD"
  }
}
</script>

Enter fullscreen mode Exit fullscreen mode

That’s perfectly valid JSON. It also fails Google’s Product validation, because price has rules that JSON itself knows nothing about:

  • No currency symbol. $ belongs in priceCurrency, not in the number.
  • No thousands separator. The , makes it unparseable as a number.
  • Dot is the only decimal mark. 1.299,00 (common in the EU) is rejected.

So "$1,299.00" is three violations in one string. Google either drops the offer or flags the whole item invalid.

The fix

"offers": {
  "@type": "Offer",
  "price": "1299.00",
  "priceCurrency": "USD"
}

Enter fullscreen mode Exit fullscreen mode

price is the bare number as a string; the symbol lives entirely in priceCurrency as an ISO 4217 code (USD, EUR, SGD, …). Schema.org’s own note is explicit about this — it even warns against Unicode symbols like U+00A0.

If your price comes out of a formatter, strip it before it goes into JSON-LD, don’t reuse the display string:

// display value: "$1,299.00" — do NOT ship this to JSON-LD
const display = new Intl.NumberFormat("en-US", {
  style: "currency", currency: "USD",
}).format(1299);

// what the schema actually needs:
const priceForSchema = (1299).toFixed(2); // "1299.00"

Enter fullscreen mode Exit fullscreen mode

The three neighbours that break with it

Once the price parses, these are the next ones Search Console complains about:

1. priceCurrency is required inside every Offer. A price with no currency is meaningless, and Google treats it as an error, not a warning.

2. availability must be a schema.org URL, not a word.

// wrong
"availability": "in stock"
// right
"availability": "https://schema.org/InStock"

Enter fullscreen mode Exit fullscreen mode

The enum values are InStock, OutOfStock, PreOrder, BackOrder, and a handful more — always the full URL.

3. priceValidUntil wants ISO 8601. Not "31 Dec 2026", but "2026-12-31". Omit it and Google will nudge you to add it for fixed-price offers.

Why the JSON linter can’t catch any of this

A JSON linter checks syntax — brackets, commas, quotes. Schema.org rules are semantic — “this string must be a bare decimal”, “that string must be one of these URLs”. They live in the vocabulary, not the grammar, so a green checkmark in your editor tells you nothing about whether Google will accept the markup.

That’s the gap you have to close with an actual structured-data validator that understands the schema.org types, not just JSON. I keep a small free schema markup validator around for exactly this — paste the JSON-LD, and it flags the price/currency/enum problems that a plain linter waves through.

The short version

  • price = bare number as a string: "1299.00". No symbol, no thousands separator, . for decimals only.
  • Currency goes in priceCurrency as an ISO 4217 code.
  • availability is a full schema.org/... URL, and dates are ISO 8601.
  • Valid JSON ≠ valid schema. Validate against the vocabulary, not just the syntax.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다