JSON-LD Extractor
Extract Schema.org JSON-LD structured data from any webpage at the edge - rich results and SEO markup inspection
Fetch a webpage and extract application/ld+json structured data blocks for Schema.org / rich-results debugging from Cloudflare's edge.
Features
- Parses all
script[type="application/ld+json"]blocks - Collects
@typevalues including nested@graph - Reports invalid JSON blocks without failing the whole response
- Caps extraction at 25 script blocks
- Stateless; no bindings required
API Reference
GET /extract
Fetches HTML and returns parsed JSON-LD blocks plus discovered types.
Prop
Type
Example Request
curl "https://your-worker.workers.dev/extract?url=https://www.cloudflare.com"Success Response
url string
Final page URL after redirects
count number
Number of JSON-LD script blocks found
types string[]
Sorted unique @type values across all blocks (including @graph)
blocks array
Per-block results with index, types, data, and optional parseError
{
"url": "https://www.cloudflare.com/",
"count": 2,
"types": ["Organization", "WebSite"],
"blocks": [
{
"index": 0,
"types": ["Organization"],
"data": {
"@context": "https://schema.org",
"@type": "Organization",
"name": "Cloudflare"
}
},
{
"index": 1,
"types": ["WebSite"],
"data": {
"@context": "https://schema.org",
"@graph": [{ "@type": "WebSite", "name": "Cloudflare" }]
}
}
]
}Error Response
{
"error": "Expected HTML, got content-type: application/json",
"code": "NOT_HTML"
}Error Codes
400- Missing or invalidurl(INVALID_URL)400- Response was not HTML (NOT_HTML)502- Fetch failed (FETCH_ERROR)
Use Cases
- Debug rich results / Schema.org markup
- Inventory Organization, Product, Article entities on a site
- Complement Open Graph previews with structured data
- Build SEO auditing tools on Workers
Limitations
- Does not execute JavaScript-injected JSON-LD
- Caps extraction at 25 script blocks
- Does not validate against Schema.org vocabularies
- Invalid JSON blocks are reported via
parseErrorrather than failing the request
Use in your project
Copy these files into an existing Worker. Prefer Deployment to try the full experiment first. Source: apps/experiments/json-ld-extractor.
import { MAX_BLOCKS } from "../constants/defaults";import type { ExtractResponse, JsonLdBlock } from "../types/extract";function collectTypes(value: unknown, out: Set<string>): void { if (!value || typeof value !== "object") return; if (Array.isArray(value)) { for (const item of value) collectTypes(item, out); return; } const obj = value as Record<string, unknown>; const t = obj["@type"]; if (typeof t === "string") out.add(t); else if (Array.isArray(t)) { for (const item of t) { if (typeof item === "string") out.add(item); } } if (Array.isArray(obj["@graph"])) { for (const item of obj["@graph"]) collectTypes(item, out); }}/** * Extract application/ld+json script blocks from HTML. */export function extractJsonLd(url: string, html: string): ExtractResponse { const re = /<script\b[^>]*type\s*=\s*["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi; const blocks: JsonLdBlock[] = []; const allTypes = new Set<string>(); let match: RegExpExecArray | null; let index = 0; while ((match = re.exec(html)) !== null && blocks.length < MAX_BLOCKS) { const raw = match[1].trim(); try { const data: unknown = JSON.parse(raw); const types = new Set<string>(); collectTypes(data, types); for (const t of types) allTypes.add(t); blocks.push({ index, types: [...types], data, }); } catch (e) { blocks.push({ index, types: [], data: null, parseError: e instanceof Error ? e.message : "Invalid JSON", }); } index += 1; } return { url, count: blocks.length, types: [...allTypes].sort(), blocks, };}Deployment
Deploy
Follow the deployment wizard. No bindings or secrets required.
Test your deployment
curl "https://your-worker.workers.dev/extract?url=https://example.com"Local Development
cd apps/experiments/json-ld-extractor
npm install
npm run devTest locally:
curl "http://localhost:8787/extract?url=https://www.cloudflare.com"