# Fix structured data: add JSON-LD and Open Graph to your home page

> Structured data states facts about your site in a form machines do not have to guess: your name, what the site is, your logo and profiles in JSON-LD, and the title and summary to show when a page is shared in Open Graph tags.

Checked 2026-09-30 against Agent-Readiness Grade 1.3.0. HTML version: https://grade.agentexchange.work/fix/structured-data

## What the grade checks

- The same home-page fetch as the readability check. **1 point** for at least one `<script type="application/ld+json">` block that parses as JSON (any `@type`); **1 point** when both `og:title` and `og:description` are present.
- The meta description is reported in the evidence but not scored. A JSON-LD block that does not parse is reported as unparsable and earns nothing.

2 of the 12 points.

## Why it matters for AI agents and crawlers

[Google](https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data) uses structured data to understand page content and recommends JSON-LD; the vocabulary is [schema.org](https://schema.org/docs/gs.html).

[Next.js's JSON-LD guide](https://nextjs.org/docs/app/guides/json-ld) describes JSON-LD as structured data that search engines and AI use to understand a page beyond its text, and renders it as a script tag in the page.

The [Open Graph protocol](https://ogp.me/) names `og:title`, `og:type`, `og:image` and `og:url` as its basic properties; `og:description` is optional there, but it is the summary previews show and the second half of this check.

## How to fix it

### 1. Add a JSON-LD block

Describe the organization and the website on the home page; add Product, FAQPage or Article on the pages they describe.

HTML head:

```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#org",
      "name": "Example Co",
      "url": "https://example.com/",
      "logo": "https://example.com/logo.png",
      "sameAs": ["https://www.linkedin.com/company/example-co"]
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#website",
      "name": "Example Co",
      "url": "https://example.com/",
      "publisher": { "@id": "https://example.com/#org" }
    }
  ]
}
</script>
```

### 2. Add Open Graph tags

Both `og:title` and `og:description` are needed for the point; add `og:type`, `og:url` and `og:image` for complete previews.

HTML head:

```html
<meta property="og:title" content="Example Co: invoicing for small agencies">
<meta property="og:description" content="Send invoices, track payments and chase late clients automatically. Plans from $12 a month.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.com/">
<meta property="og:image" content="https://example.com/og.png">
```

### 3. Put them in your stack

Both go in the `<head>` of the home page as the server sends it.

#### Static site

Paste both into the `<head>` of your home page template.

#### WordPress

SEO plugins such as Yoast SEO, Rank Math and AIOSEO output JSON-LD and Open Graph tags; check that their schema and social settings are on, then confirm with curl. Without a plugin, print the block from `wp_head`:

wp-content/mu-plugins/org-schema.php:

```php
<?php
add_action( 'wp_head', function () {
    if ( ! is_front_page() ) {
        return;
    }
    $ld = array(
        '@context' => 'https://schema.org',
        '@type'    => 'Organization',
        'name'     => get_bloginfo( 'name' ),
        'url'      => home_url( '/' ),
    );
    echo '<script type="application/ld+json">' . wp_json_encode( $ld, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . '</script>' . "\n";
} );
```

#### Next.js

Set Open Graph with the metadata API and render JSON-LD as a script tag, escaping `<` as the [Next.js guide](https://nextjs.org/docs/app/guides/json-ld) recommends:

app/page.tsx:

```tsx
export const metadata = {
  title: 'Example Co: invoicing for small agencies',
  description: 'Send invoices, track payments and chase late clients automatically.',
  openGraph: {
    title: 'Example Co: invoicing for small agencies',
    description: 'Send invoices, track payments and chase late clients automatically.',
    url: 'https://example.com/',
    type: 'website',
  },
}

export default function Home() {
  const jsonLd = { '@context': 'https://schema.org', '@type': 'Organization', name: 'Example Co', url: 'https://example.com/' }
  return (
    <main>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, '\\u003c') }}
      />
      {/* page content */}
    </main>
  )
}
```

#### Cloudflare

For a site behind a Worker, `HTMLRewriter` can add the block to every HTML response without touching templates:

Cloudflare Worker:

```js
const LD = JSON.stringify({ "@context": "https://schema.org", "@type": "Organization", name: "Example Co", url: "https://example.com/" }).replace(/</g, "\\u003c");

export default {
  async fetch(request, env) {
    const res = await env.ASSETS.fetch(request); // or fetch(request) to your origin
    return new HTMLRewriter()
      .on("head", { element(el) { el.append(`<script type="application/ld+json">${LD}</script>`, { html: true }); } })
      .transform(res);
  },
};
```

## How to verify

Count the JSON-LD blocks and list the Open Graph properties the home page sends.

```sh
curl -s https://example.com/ | grep -c 'application/ld+json'
curl -s https://example.com/ | grep -o 'property="og:[a-z_:]*"' | sort -u
```

At least one JSON-LD block, and both `og:title` and `og:description` in the list.

Re-grade: https://grade.agentexchange.work/grade?url=example.com&fresh=1

## Questions

### Which schema.org type should the home page use?

Organization (or a more specific type such as LocalBusiness) plus WebSite is the common pair; add Product, FAQPage or Article on the pages they describe. The grade accepts any JSON-LD block that parses.

### Does invalid JSON-LD count?

No. A block that does not parse is reported as unparsable and earns nothing; trailing commas and unescaped quotes are the usual causes.

### Do I need both og:title and og:description?

For the point, yes: the check requires both on the home page.

## Sources

- [Introduction to structured data markup in Google Search](https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data) (Google Search Central)
- [schema.org: getting started](https://schema.org/docs/gs.html) (schema.org)
- [The Open Graph protocol](https://ogp.me/) (ogp.me)
- [How to implement JSON-LD in your Next.js application](https://nextjs.org/docs/app/guides/json-ld) (Next.js)

## Related

- [Readable without JavaScript](https://grade.agentexchange.work/fix/readable-without-javascript.md): The home page as a plain fetch sees it: enough visible text, no empty #root, no challenge.
- [llms.txt](https://grade.agentexchange.work/fix/llms-txt.md): A Markdown guide to your key pages at /llms.txt, served as text, not as your HTML 404.
- [All fix guides](https://grade.agentexchange.work/fix)
