squirrelscan
GitHub

What is JSON-LD? Syntax, @id and common errors

JSON-LD is the JSON format for linked data that most sites use for structured data. The syntax, why every @id should be an absolute URL, and what to check.

JSON-LD (JSON for Linking Data) is a W3C format for writing linked data as JSON, and the format Google recommends for structured data, placed in a <script type="application/ld+json"> block.

A block names its vocabulary with @context, usually https://schema.org, and what each node describes with @type. A node can carry an @id, an identifier other nodes point at, and a @graph array holds several nodes in one block. Because the block sits beside the page content rather than inside it, nothing on the rendered page changes when it is wrong.

Category: Structured Data

Why it matters

Two faults show up. A syntax error, usually a trailing comma, an unescaped quote or a raw newline inside a string, makes the whole block unreadable. A validation error means the JSON parsed but a type is missing a property a consumer expects. Identifiers fail more quietly: "@id": "#organization" resolves against each page’s own URL, so the home page and an article declare two different organizations, and the entity never builds up across the site.

How squirrelscan checks it

schema/json-ld-valid parses each block and reports syntax errors and missing or invalid properties, with the property path for each. Nodes that share an @id on the same page are checked together, which is how generators such as Yoast link their nodes. Two rules read the entity map built from the whole crawl: schema/entity-id-format warns about an @id that is still not an absolute URL after the audit tries to resolve it against its page (urn:, doi:, info:, tag: and isbn: identifiers are accepted), and schema/entity-dangling fails a reference to an @id that no crawled page declares. sameAs links are exempt, because they point outside the site by design.

RuleWhat it flagsSeverity
Invalid JSON-LD syntax: what breaks it and how to fixA structured data block that will not parse, or parses but omits properties schema.org requires. squirrel names the exact type and property.warning
A bare #organization is one entity per page, not one entityA relative @id resolves against the page it sits on, so the same fragment on forty pages is forty identifiers. The markup validates and the entity still never accumulates.warning
A publisher that points at nothingA reference to an @id that nothing in the crawl declares. squirrel checks against the whole site, so a publisher the homepage declares still resolves.error

How to fix it

Write every @id as a full URL, and declare each node you reference on every page that references it (see the dangling reference fix):

{
  "@context": "https://schema.org",
  "@graph": [
    { "@type": "Article", "@id": "https://example.com/post#article",
      "publisher": { "@id": "https://example.com/#organization" } },
    { "@type": "Organization", "@id": "https://example.com/#organization",
      "name": "Example Ltd", "url": "https://example.com/" }
  ]
}

Serialise the block from an object with JSON.stringify, and fix a relative @id in the template that builds it rather than page by page.

Run the check

squirrel audit https://example.com --rule-include schema/json-ld-valid,schema/entity-id-format,schema/entity-dangling

A signed-out local audit costs nothing.

  • Structured data: Machine-readable statements about a page’s content, usually schema.org vocabulary in JSON-LD, that search engines and agents can parse.
  • @id (JSON-LD node identifier): The JSON-LD @id that gives a node a stable IRI, so other nodes and pages can reference the same entity instead of redeclaring it.

References

Type to search…

↑↓ navigate↵ openesc close