Back to Blog
Guides16 min read2 September 2026

Open Graph vs JSON-LD: What Each One Is For

Open Graph and JSON-LD look like they say the same thing. They answer different questions. What each is read by, and what happens when they disagree.

By AI Schema Gen Team

Look at the two blocks of code most pages carry in their head section and they seem to say the same thing twice. Open Graph tags declare a title, a description, an image and a type. JSON-LD declares a name, a description, an image and a type. Same four facts, written twice, in two different formats.

It is a reasonable thing to be annoyed by, and it leads to the obvious question: can one replace the other?

No, and the reason is not that one is better. The two formats answer different questions, and the four fields that look duplicated are the least interesting thing about either of them. The more useful question, which almost nobody asks, is what happens when those four fields disagree.

The difference in one line: one object, or many things

Open Graph turns your page into a single object.

That is not a summary, it is the protocol's own description. The Open Graph protocol was built at Facebook, and its specification opens with the sentence:

The Open Graph protocol enables any web page to become a rich object in a social graph.

One page, one object. The spec lists four required properties, and one of them is og:url, described as the canonical URL that serves as the object's permanent ID in the graph. One identifier, for the page, because the page is the thing being described.

JSON-LD does something different in kind. It describes the things the page is about, however many of them there are, and the relationships between them.

Here is the same page both ways. A recipe page, in Open Graph:

<meta property="og:type"        content="article" />
<meta property="og:title"       content="Slow-Roast Tomato Soup" />
<meta property="og:description" content="A four-ingredient soup that takes ten minutes of work." />
<meta property="og:image"       content="https://example.com/soup.jpg" />
<meta property="og:url"         content="https://example.com/recipes/tomato-soup" />

That is the whole page, flattened into one object with five facts about it. There is nowhere to put the cook time, no way to say who wrote it, and no way to say that the author is the same person who wrote forty other recipes on the site.

The same page in JSON-LD:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Recipe",
      "@id": "https://example.com/recipes/tomato-soup#recipe",
      "name": "Slow-Roast Tomato Soup",
      "cookTime": "PT50M",
      "recipeYield": "4 servings",
      "recipeIngredient": ["2kg ripe tomatoes", "1 head garlic", "olive oil", "sea salt"],
      "author": { "@id": "https://example.com/about#priya" }
    },
    {
      "@type": "Person",
      "@id": "https://example.com/about#priya",
      "name": "Priya Raman",
      "sameAs": ["https://www.linkedin.com/in/priyaraman"]
    },
    {
      "@type": "WebPage",
      "@id": "https://example.com/recipes/tomato-soup",
      "primaryImageOfPage": { "@id": "https://example.com/recipes/tomato-soup#img" }
    }
  ]
}

Three separate things now exist: a recipe, a person, and a page. The recipe points at the person by identifier, so the same author on forty recipes is one person rather than forty strings that happen to match. The ingredients and cook time are real fields, not prose a machine has to guess at.

This is the whole difference. Open Graph has exactly one slot, and the page goes in it. JSON-LD has as many slots as you need, and they can point at each other. No amount of Open Graph tags gets you the second thing, because the format has no way to express a second thing at all.

Which is also why the reverse substitution fails. JSON-LD cannot replace Open Graph either, because nothing that unfurls a shared link reads it.

The four fields that look duplicated

Now the practical part. Both formats carry a title, a description, an image and a type. What happens when they disagree?

They are not competing, because different systems read each one, and in most cases neither overrules the other. But the results show up in different places, and knowing which is which saves a lot of confused debugging.

Title. og:title is what appears when the link is shared. The name or headline in your JSON-LD is what search and AI systems read as the name of the thing. These can legitimately differ: a punchy share title and a precise entity name are not always the same string, and neither is wrong. Google does read og:title, though not for the reason most people assume, which is the next section.

Description. og:description is the grey line under the preview card. A schema description is a fact about an entity. Google generates search snippets from page content and the meta description rather than from either of these, so a mismatch between your Open Graph description and your schema description has no effect on your snippet.

Image. This is where the two genuinely do different jobs and people most often expect the wrong one to work. og:image is the picture in the share card. It is not a candidate for a rich result thumbnail. Schema image on a Recipe or Product is what feeds the image in a rich result. Setting a beautiful og:image and no schema image gives you a great-looking Slack preview and nothing in search.

Type. og:type comes from a small fixed list (article, website, video.movie, book, profile and a handful more). Schema @type comes from schema.org's full vocabulary of hundreds of types. og:type of article alongside a schema @type of Recipe is not a contradiction, it is two vocabularies of very different sizes describing the page at different resolutions. Do not force og:type to match your schema type. It usually cannot.

The short version: a disagreement between these two blocks is almost never an error. The real errors are when one of them is missing, or when either one disagrees with the page a human sees.

Different readers, at different moments

The formats also get read at different times, which explains most of the odd behaviour people run into.

JSON-LD is read at crawl time. A search or AI crawler fetches your page on its own schedule, reads the markup, and updates its understanding whenever it next comes back.

Open Graph is read at share time. Nothing reads your Open Graph tags until somebody pastes your link somewhere. At that moment the platform sends its own crawler to fetch the page and build a card. Facebook's is identified in its sharing documentation as facebookexternalhit. LinkedIn, Slack, Discord, WhatsApp and iMessage all do their own version of the same thing.

Two practical consequences follow, and neither has a schema equivalent.

Previews get cached, sometimes stubbornly. Facebook's documentation is explicit that "images are cached based on the URL and won't be updated unless the URL changes," and points at its Sharing Debugger to force a fresh scrape. So the classic problem, where you fix your Open Graph image and the old one keeps appearing, is a cache, not a mistake in your tags. Schema has nothing like this. Fix your JSON-LD and the correction lands whenever the page is next crawled.

Blocking a crawler blocks the preview. If facebookexternalhit cannot fetch the page, there is no card. This catches staging sites, pages behind logins, and sites with aggressive bot filtering, and it looks exactly like broken tags.

Does Google read Open Graph?

Yes, and this is the single most misunderstood point in the whole topic, in both directions. People either insist Google ignores Open Graph completely, or assume it is a route to a rich result. Both are wrong.

Google's title link documentation lists the sources it draws on when generating the clickable title in a search result:

Content in <title> elements, Main visual title shown on the page, Heading elements, such as <h1> elements, Content in og:title meta tags, Other content that's large and prominent through the use of style treatments, Other text contained in the page, Anchor text on the page, Text within links that point to the page

og:title is on that list, in Google's own words. So Open Graph is not invisible to Google.

What it cannot do is produce a rich result. Google's structured data guidelines name the three formats it accepts:

JSON-LD (recommended), Microdata, RDFa

Open Graph is not among them, and never has been. No quantity of og: tags will get you review stars, a recipe card, a product panel or a breadcrumb trail. If you want those, the markup has to be in one of the three formats above, which in practice means JSON-LD.

Hold both facts at once: Google may use your og:title as one input among many when deciding what to call your page, and your Open Graph tags will never make you eligible for a single rich result.

Do you still need Twitter Card tags?

Usually not, and this is the one place the duplication genuinely is avoidable.

X's card documentation describes a fallback: the card processor first looks for the X-specific property, and where it is absent, falls back to the matching Open Graph property. So twitter:title falls back to og:title, and so on. A page with complete Open Graph tags and no twitter: tags at all will still produce a card.

Add twitter: tags when you want something genuinely different there, most often a different card size via twitter:card. Otherwise the Open Graph set covers it, and maintaining two copies of the same four strings is how they drift apart.

Do AI systems read Open Graph?

Here the honest answer is thinner than most articles will tell you.

Open Graph tags sit in the head of the page as plain HTML, so anything that fetches and reads your page can see them, and they are unusually cheap to read: four short strings, no content extraction required. That is the argument for why a system building a quick summary of a page might use them.

What does not exist is documentation from the AI providers confirming that they do, or saying how it is weighted. So treat "AI systems read your Open Graph tags" as plausible and undocumented, not as an established fact, and be sceptical of any article that puts a number on it.

What can be said without guessing is narrower and more useful. Open Graph tags are a deliberate, one-line, machine-readable statement of what a page is, written by you rather than inferred. That is the same category of signal as a good <title> and a real meta description, and it costs nothing.

Our own AI readiness scoring treats it that way. A page missing any of og:title, og:description or og:image counts as a defect in the Read pillar, the part of the score covering whether a machine can make sense of the page in front of it. It is deliberately a modest weight rather than a headline one, and Read is 15 points of the total against 45 for Understand, which is where schema markup mostly lives. The Read pillar guide covers the rest of what that section checks.

That ratio is the honest summary of the two formats' relative weight. Open Graph is worth having and cheap to add. Schema is where the substance is.

What only one of them can do

Only Open Graph can:

  • Control how your link looks when it is pasted into Slack, LinkedIn, WhatsApp, Discord or a message
  • Set the image on that preview card
  • Give a share a deliberate title that differs from the page title

Only JSON-LD can:

  • Make you eligible for any Google rich result
  • Describe more than one thing on a page
  • State a relationship between two things, such as this article's author being that person
  • Carry facts with no prose equivalent: a cook time, a price, an opening hour, a registration number
  • Give a thing a stable identifier that other pages can point at

The lists barely touch. That is the answer to the substitution question, and it is why the right move is both.

There is one honest caveat about the sharing side. Platforms change how they render previews, and some have quietly reduced how much of a card they show. Open Graph controls what a platform has to work with, not how much of it the platform chooses to display.

Where each block actually comes from on your site

Worth knowing, because when one of them is wrong the fix is almost never in the page template.

On WordPress, Open Graph tags are usually emitted by whichever SEO plugin is installed, from its social settings, with a per-post override in the editor sidebar. The trap is that themes and page builders sometimes emit their own set too, so you end up with two og:title lines and a preview that picks one apparently at random. The JSON-LD comes from a different source again, either the same SEO plugin's schema settings or a dedicated schema tool. Three possible emitters, and none of them knows about the others.

On Next.js and similar frameworks, Open Graph is generated from the framework's own metadata handling, so it is code rather than a settings screen, and it is usually correct because it is generated from the same data as the page. JSON-LD is normally hand-added as a script in the page, which is why it is the half more likely to be missing, stale, or present on only some templates.

On Shopify and hosted platforms, the theme emits Open Graph from the product or article record, which is usually fine and mostly out of your hands. Schema is either the theme's built-in output or an app, and the built-in output is commonly incomplete.

The practical rule in all three cases: find the emitter before editing the output. Deleting a duplicate tag from a template while the setting that generates it stays switched on means it returns at the next update.

Common mistakes

Expecting og:image to appear in search results. It is the share card image. The rich result image comes from schema image on the entity, and the two are unrelated.

Assuming Open Graph is a ranking or rich-result signal. It is neither. It is not one of Google's three accepted structured data formats, and adding it will not make you eligible for anything.

Treating them as interchangeable and picking one. A site with only Open Graph is invisible to rich results and describes nothing beyond the page itself. A site with only JSON-LD produces a bare link with no card everywhere it gets shared.

Maintaining twitter: tags that duplicate og: tags exactly. The fallback already handles it, and two copies drift.

Forcing og:type to match your schema @type. The Open Graph type list is short and fixed. Most schema types have no Open Graph equivalent, and that is fine.

Blocking the social crawlers, then debugging the tags. If facebookexternalhit cannot reach the page there is no preview, however perfect the markup.

Chasing a stale preview by re-editing correct tags. Check the platform's cache and force a refresh first, as the tags are usually already right. This is covered in more detail in our guide to common schema errors, which deals with the same class of "the markup is fine, something else is wrong" problem.

Letting either block contradict the visible page. A title or image in the head that appears nowhere on the page is the one genuine mismatch worth fixing.

How to check both on one page

Ten minutes, one page, both formats.

1. Read the head. View source and find every <meta property="og: line and every <script type="application/ld+json"> block. Note which of the four Open Graph basics are missing: title, description, image, url.

2. Check the share preview separately from the search preview. These are two different tests. Paste the URL into a private Slack message or use a platform's own sharing debugger for the card. Use the Rich Results Test for what Google can do with your JSON-LD. A pass in one says nothing about the other.

3. Confirm the image is a real, reachable, absolute URL. Relative paths and images behind a login are the most common reason a card renders blank. Open the URL from the markup in a fresh browser tab.

4. Count the things your page is about. If the page describes more than one thing, an article and its author, a product and its brand, check that your JSON-LD actually contains both as separate nodes rather than one flattened block. This is the capability Open Graph does not have, so it is worth confirming you are using it. Our Organization schema guide covers the identifier pattern that connects them.

5. Check for duplicate or conflicting blocks. Two plugins each emitting Open Graph tags is common and produces two og:title lines, with the platform picking one unpredictably.

6. Validate the JSON-LD properly. The Rich Results Test covers Google's supported features; validator.schema.org checks general vocabulary correctness for everything else. See our supported types for what is currently eligible.

Frequently Asked Questions


AI Schema Gen generates the JSON-LD half automatically, as a connected set of entities rather than one flat block, and flags pages missing their sharing details as part of your AI readiness score. Start free and see what your pages currently declare.

Is your site ready for AI?

Get a free readiness score in under a minute. No signup, no card.

Run the free check