Why doesn’t AI mention your brand? Find out which half is brokenGet a free report
LLMention
Explainer

The entity graph: Organization vs WebSite vs SoftwareApplication

Most entity markup is three disconnected blocks. The part that makes it a graph is the identifiers and relations — and the part most sites skip is deciding what the product is.

By Alex Xu10 min read

There is a specific kind of structured data that is easy to add and hard to get right: the entity graph. Almost every guide shows a single block with Organization and WebSite, the page validates, and the site is declared done. What the block usually lacks is the part that makes it a graph — identifiers and relations — and the part that distinguishes the company from the site from the product.

This article is about the three nodes that most product sites need, what each one answers, how to connect them with @id, and the mistakes that leave a technically valid graph saying less than it appears to.

Three questions, three nodes

The nodes are not synonyms for "my site". They answer different questions, and collapsing them into one loses the distinctions an engine needs.

NodeQuestion it answersTypical properties
OrganizationWho is responsible for this site and this product?name, url, logo, sameAs, founder, email
WebSiteWhat is the site, and who publishes it?url, name, publisher, inLanguage
SoftwareApplicationWhat is the product, and what can it do?name, applicationCategory, operatingSystem, offers, publisher

A useful rule of thumb: if the thing could plausibly exist without the website, it is not the WebSite. A company and a product can both exist without a particular domain, which is why they get their own nodes. The site is the surface through which both are read.

The part that makes it a graph: @id

@id gives a node a stable identifier — usually the site URL plus a fragment — and lets other nodes refer to it instead of repeating it. A relation such as {"publisher": { "@id": "https://example.com/#organization" }} says "this is the same organisation declared above", not "here is another organisation". Without those references, three blocks on one page are three disconnected claims; with them, they are one entity described in three roles.

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#organization",
      "name": "Example Labs",
      "url": "https://example.com/",
      "logo": "https://example.com/logo.png",
      "sameAs": ["https://github.com/example-labs"],
      "founder": { "@id": "https://example.com/#founder" }
    },
    {
      "@type": "Person",
      "@id": "https://example.com/#founder",
      "name": "Jane Example",
      "jobTitle": "Founder"
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#website",
      "url": "https://example.com/",
      "name": "Example Labs",
      "publisher": { "@id": "https://example.com/#organization" }
    },
    {
      "@type": "SoftwareApplication",
      "@id": "https://example.com/#app",
      "name": "Example Audit",
      "applicationCategory": "BusinessApplication",
      "operatingSystem": "Web",
      "publisher": { "@id": "https://example.com/#organization" },
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "USD"
      }
    }
  ]
}

The exact properties are less important than the shape: one node per thing, an identifier per node, and references between them. The graph should read like a sentence — this organisation publishes this site and this product — not like three brochures stapled together.

Mistakes that pass validation and still say nothing

MistakeWhat it costs
Three nodes, no @id, no relationsThree unconnected claims; an engine cannot tell they describe one thing.
Using WebSite for the productThe site is described, the product is not; there is nothing to attach an app category or offer to.
Brand name in name differs from the visible titleThe page and the markup make different claims, which is the ambiguity markup was meant to remove.
sameAs pointing at the site itself or at a profile that does not list the brandA link with no corroboration; it adds noise instead of identity.
Product node without a publisherThe product floats: no one is accountable for it, and it is not attached to the organisation.
Two Organization nodes with different identifiersThe graph contradicts itself; resolution becomes a guess.
JSON-LD injected after hydrationThe markup is absent from the server response a crawler parses.
Invented addresses, awards or ratingsFalse structured data is a trust and compliance problem, not a markup problem.

How this scanner scores it — and what the score means

The rule is jsonld-entity, worth 4 points in the machine readability dimension. The condition is deliberately simple: parsed JSON-LD must contain an @type of Organization, WebSite, Person or LocalBusiness. Nested nodes count, so a Person inside an Article’s author property satisfies it.

That is a floor, not a verdict. The rule page says so explicitly: a page can pass without declaring an Organization anywhere, and the evidence line names the types it matched rather than asserting that a brand entity exists. The check is cheap and honest about being cheap; the work of building a graph is still the work.

There is a second, separate rule for page-type markup (jsonld-content), which looks for FAQPage, Article, Product, SoftwareApplication and similar. Passing one does not imply the other: a site can declare a perfect Organization and still not say what kind of page the reader is on.

How to check your own graph

The first requirement is that the data is in the response, not only in the browser. Fetch the page as a crawler would and parse what comes back.

# 1. Is there a JSON-LD block in the server response?
curl -sS https://your-domain.com/ | grep -c 'application/ld+json'

# 2. Extract it and check that it parses
curl -sS https://your-domain.com/ \
  | python -c "import sys,json,re; html=sys.stdin.read(); \
      blocks=re.findall(r'<script[^>]*application/ld\\+json[^>]*>(.*?)</script>', html, re.S); \
      [json.loads(b) for b in blocks]; print(len(blocks), 'block(s) parsed')"

# 3. List the @type values and the @id references
curl -sS https://your-domain.com/ | grep -oE '"@type" *: *"[^"]+"' | sort -u

Then check consistency by hand: every @id reference should resolve to a node in the same graph; the brand name should match the visible heading; the product should have a publisher; sameAs should point at profiles a reader can open. A validator catches syntax; only reading the graph catches a contradiction.

What a graph changes, and what it does not

Good entity markup makes disambiguation cheaper: a model does not have to infer whether the brand name, the domain and the product are one thing or three. That is a real benefit for products with common names or with a name close to another product’s, and it is the part of GEO that is closest to traditional SEO hygiene.

It does not guarantee understanding, ranking or citation. Entity resolution is an inference from many sources, and one page’s JSON-LD is one input. What the markup can do is remove avoidable ambiguity and make the claim auditable — which is exactly what a scanner can check, and what a reader can verify.

Questions about the entity graph

Do I need all three nodes?

Not always, but most product sites need at least Organization and WebSite, and a tool or app should describe itself with SoftwareApplication rather than being folded into WebSite. The point is not the count; it is that each node answers a different question and the relations say how they belong together.

Does adding the markup guarantee the engine understands my brand?

No. Structured data is a set of claims; entity resolution is an inference the engine makes from those claims, the page text, links and other sources. The markup removes avoidable ambiguity. It does not force a conclusion.

Why does the scanner pass a page with only a Person node?

Because its entity check accepts any Organization, WebSite, Person or LocalBusiness node, including one nested inside an Article's author property. That is a floor, not a proper entity graph: a page can pass the check and still have no declared organisation or product. The evidence line names what it saw so the pass is not mistaken for more.

Evidence and sources

GEO can boost visibility by up to 40% overall in generative engine responses, and in the paper's Table 1 the best of the tested methods improve on the no-optimization baseline by 41% and 28%. — Generative Engine Optimization, KDD 2024

Every figure in this piece comes from the published study or from the rule set the scanner executes; both are linked, and both can be re-run.

Primary sources

Alex Xu — Maintainer, LLMention

I build and maintain LLMention: the scanner, the rule set and the study behind it. The work is published as source, so the method can be checked rather than taken on trust.

How this site is written and funded →

Read next