Technical guide · reviewed September 2026
How to extract JSON-LD structured data from a website
JSON-LD is JSON embedded in a page, usually in a <script type="application/ld+json"> element. It can describe products, articles, breadcrumbs, organizations, and other entities. Treat it as publisher-provided structured data: useful for discovery, but not automatically complete, current, or authoritative for every field.
Inspect a block in DevTools
For a one-off check on a page you are allowed to inspect, parse every block and keep malformed entries separate instead of letting one bad block abort the whole result.
const blocks = [...document.querySelectorAll('script[type="application/ld+json"]')];
const parsed = blocks.flatMap((element, index) => {
try { return [{ index, value: JSON.parse(element.textContent) }]; }
catch { return [{ index, error: 'invalid_json' }]; }
});
console.log(JSON.stringify(parsed, null, 2));
Use the page’s visible content and the schema vocabulary documented at Schema.org when deciding how to normalize a field. Do not assume that a schema block proves the value is visible, available, or eligible for a search feature.
Why raw HTTP and browser output can differ
Some sites include JSON-LD in the initial response; others insert it after JavaScript runs. Compare a raw response with the DOM after a normal browser load before selecting your method. If the required public block is client-rendered, a browser-enabled request can help.
// Raw HTTP: this synthetic response may contain no JSON-LD.
const raw = await fetch('https://demo.example/products/widget');
const html = await raw.text();
console.log(html.includes('application/ld+json'));
// Browser DOM: the page may add a block after scripts run.
console.log(document.querySelectorAll('script[type="application/ld+json"]').length);
Extract with ExtractAPI
ExtractAPI parses valid JSON-LD blocks from the page and returns them in data.structuredData. The field name is structuredData, not jsonld; malformed blocks are skipped and response-size limits still apply.
curl -X POST https://extractapi.app/v1/extract \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://demo.example/products/widget","javascript":true}'
Here is a synthetic, shortened response:
{
"status": "ok",
"url": "https://demo.example/products/widget",
"data": {
"title": "Widget Pro | Demo Store",
"structuredData": [
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Widget Pro",
"offers": {"@type":"Offer","price":"49.00","priceCurrency":"USD"}
},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": []
}
],
"mainContent": "Widget Pro In stock $49.00"
}
}
Normalize without losing provenance
Keep the original block, source URL, extraction time, and schema type alongside normalized fields. A product can have multiple offers or variants, and a page can publish more than one entity.
function productOffers(structuredData) {
return structuredData
.filter(block => block && block['@type'] === 'Product')
.flatMap(product => {
const offers = Array.isArray(product.offers) ? product.offers : [product.offers];
return offers.filter(Boolean).map(offer => ({
name: product.name || null,
price: offer.price ?? null,
currency: offer.priceCurrency ?? null
}));
});
}
Common failure modes
- Multiple blocks: iterate all blocks; do not assume the first block is the Product or Article you need.
- Malformed JSON: record an
invalid_jsoncategory and continue. Never execute the text as JavaScript. - Client rendering: raw HTTP may miss a block that appears only after scripts run; browser mode may still be unable to load a protected or unavailable dependency.
- Stale or conflicting values: compare structured data with visible text where correctness matters; preserve unknown values rather than inventing defaults.
- Rate limits and permissions: use a small allowlist, cache observations, respect target rules, and stop on an access control.
FAQ
Does every page publish JSON-LD?
No. A page may use microdata, RDFa, visible HTML only, or no structured data. A missing array is a valid result.
Can JSON-LD be trusted as the current price?
Not by itself. Validate the page, timestamp, currency, variant, availability, and visible value required by your use case.
Does ExtractAPI return JSON-LD from JavaScript-rendered pages?
It can return valid blocks present in the rendered DOM when browser mode succeeds. Protected pages, failed dependencies, limits, and malformed blocks can still produce no structured data.