# Schema.org Structured Data Extractor: JSON-LD, OpenGraph (`actose/structured-data-extractor`) Actor

Extract schema.org JSON-LD, microdata, RDFa, OpenGraph and Twitter Cards from any URL list, flattened to ready-to-use fields: name, price, rating, dates, ingredients, salary. Filter by Product, Event, Recipe, JobPosting. No login. MCP-ready for AI agents. Errors are free. $1 per 1,000 pages.

- **URL**: https://apify.com/actose/structured-data-extractor.md
- **Developed by:** [ABM](https://apify.com/actose) (community)
- **Categories:** SEO tools, Developer tools, E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.85 / 1,000 page parseds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Schema.org Structured Data Extractor: JSON-LD, Microdata, RDFa, OpenGraph, Twitter Cards

**$1 per 1,000 pages. Errors, blocked pages and pages without structured data are free.**

Paste a list of URLs and get back the **schema.org structured data** of every page — **JSON-LD, microdata, RDFa, OpenGraph and Twitter Cards** — normalised to one format and **flattened into ready-to-use columns**: name, price, currency, availability, rating, review count, brand, SKU/GTIN, author, dates, location, address, ingredients, cooking times, salary, employment type and more.

Filter by schema.org type (**Product, Event, Recipe, JobPosting, Article, LocalBusiness, Movie...**) and get only the entities you need, nested ones included (a `Product` inside an `ItemList`, a `Recipe` inside a `WebPage`).

- No browser, no API key, no login: one light HTTP request per page.
- Every syntax in one pass, normalised to the same JSON-LD shape.
- Flat fields for spreadsheets, plus the full objects for developers.
- Markdown summary per page, ready for LLMs and RAG.
- `onlyNew` mode for scheduled runs: you only get (and pay for) pages whose data changed.
- MCP-ready for Claude, Cursor, ChatGPT and other AI agents.

### How much does it cost?

Pay per result, no monthly rental: **$0.001 per parsed page** (`page-parsed` event).

| Pages | Price |
|---|---|
| 100 | $0.10 |
| 1,000 | **$1.00** |
| 10,000 | $10.00 |
| 100,000 | $100.00 |

You only pay for pages that actually delivered structured data. These are **free**: invalid URLs, duplicates, 404s, unreachable or blocked websites, pages without structured data, pages without the requested `schemaTypes`, and unchanged pages in `onlyNew` mode. The run stops cleanly when your maximum charge is reached.

### What data does it extract?

| Syntax | What is read |
|---|---|
| JSON-LD | Every `<script type="application/ld+json">` block, `@graph` expanded, tolerant to trailing commas, CDATA and HTML escaping |
| Microdata | `itemscope` / `itemtype` / `itemprop`, nested items, `content`, `href`, `src`, `datetime` values |
| RDFa Lite | `vocab` / `typeof` / `property` / `resource` |
| OpenGraph | `og:*`, `article:*`, `product:*`, `music:*`, `video:*`, `book:*`, `profile:*`, `fb:*` |
| Twitter Cards | `twitter:*` |
| Meta | `<title>`, meta description, canonical URL, language |

**Flat fields** (taken from the main entity of the page): `primaryType`, `name`, `description`, `image`, `entityUrl`, `brand`, `sku`, `gtin`, `mpn`, `price`, `priceCurrency`, `lowPrice`, `highPrice`, `availability`, `itemCondition`, `ratingValue`, `bestRating`, `ratingCount`, `reviewCount`, `author`, `publisher`, `datePublished`, `dateModified`, `startDate`, `endDate`, `eventStatus`, `eventAttendanceMode`, `locationName`, `address`, `latitude`, `longitude`, `telephone`, `openingHours`, `organizer`, `performer`, `prepTime`, `cookTime`, `totalTime`, `recipeYield`, `recipeCategory`, `recipeCuisine`, `calories`, `recipeIngredients`, `recipeInstructions`, `hiringOrganization`, `datePosted`, `validThrough`, `employmentType`, `jobLocation`, `jobLocationType`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryUnit`, `genre`, `duration`, `director`, `actors`, `contentRating`, `keywords`, `inLanguage`.

**Full data**: `mainEntity` (cleaned object), `mainEntityFlat` (dot keys like `offers.price`), `entities` (every entity with its `type`, `syntax`, `@id` and `data`), `openGraph`, `twitterCards`, `markdown`.

### Input

| Field | Description |
|---|---|
| `urls` | Page URLs (required). Bare domains are accepted. |
| `schemaTypes` | Optional filter, e.g. `["Product"]`, `["Event"]`, `["Recipe", "HowTo"]`, `["JobPosting"]`. Subtypes match too. |
| `syntaxes` | `json-ld`, `microdata`, `rdfa`, `opengraph`, `twitter` (all by default). |
| `onlyNew` | Only return pages that are new or changed since the last run. |
| `maxEntitiesPerPage` | Max entities kept per page (default 50). |
| `proxyFallback` | Retry a page once through a datacenter proxy if the site refuses the server IP (default on). |
| `maxConcurrency` / `requestTimeoutSecs` | Speed and timeout settings. |

```json
{
  "urls": [
    "https://www.imdb.com/title/tt0111161/",
    "https://www.allrecipes.com/recipe/16354/easy-meatloaf/"
  ],
  "schemaTypes": ["Movie", "Recipe"]
}
```

### Output

One dataset item per page (abridged):

```json
{
  "url": "https://www.allrecipes.com/recipe/16354/easy-meatloaf/",
  "finalUrl": "https://www.allrecipes.com/recipe/16354/easy-meatloaf/",
  "status": "ok",
  "statusCode": 200,
  "schemaTypes": ["Recipe", "NewsArticle"],
  "matchedTypes": ["Recipe"],
  "syntaxesFound": ["json-ld", "opengraph", "twitter"],
  "entityCount": 1,
  "primaryType": "Recipe",
  "name": "Easy Meatloaf",
  "image": "https://www.allrecipes.com/thmb/....jpg",
  "author": "Janet Caldwell",
  "datePublished": "1998-05-07T13:22:03.000-04:00",
  "ratingValue": 4.6,
  "ratingCount": 6820,
  "prepTime": "PT10M",
  "cookTime": "PT60M",
  "totalTime": "PT70M",
  "recipeYield": "8",
  "calories": "372 kcal",
  "recipeIngredients": ["1 ½ pounds ground beef", "1 egg", "1 onion, chopped", "..."],
  "recipeInstructions": ["Preheat the oven to 350 degrees F (175 degrees C).", "..."],
  "price": null,
  "ogTitle": "Easy Meatloaf Recipe",
  "twitterCard": "summary_large_image",
  "mainEntity": { "@type": "Recipe", "name": "Easy Meatloaf", "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.6" } },
  "mainEntityFlat": { "@type": "Recipe", "name": "Easy Meatloaf", "aggregateRating.ratingValue": "4.6" },
  "entities": [{ "type": "Recipe", "types": ["Recipe"], "syntax": "json-ld", "id": null, "data": { "...": "..." } }],
  "openGraph": { "og:title": "Easy Meatloaf Recipe", "og:type": "article" },
  "twitterCards": { "twitter:card": "summary_large_image" },
  "markdown": "# Easy Meatloaf\n\n- **Type:** Recipe\n- **Rating:** 4.6\n...",
  "changedSinceLastRun": null,
  "viaProxy": false,
  "fetchedAt": "2026-10-23T08:00:00+00:00",
  "error": null
}
```

Free rows explain what happened, with `status` set to `noStructuredData`, `noMatchingType`, `blocked`, `notFound`, `notHtml`, `error` or `invalidInput` and a human-readable `error`.

The **Overview** view gives a table (URL, type, name, price, rating, dates, image); the **Entities** view gives one row per schema.org entity.

### Use cases

#### 1. Price and stock monitoring for an e-commerce team

A pricing analyst follows 2,000 competitor product pages. Scheduled every morning with `onlyNew`, the run only returns (and charges) the products whose price, availability or rating changed, so a typical day costs a few cents instead of $2.

```json
{ "urls": ["https://shop.example.com/p/espresso-machine-x200", "..."], "schemaTypes": ["Product"], "onlyNew": true }
```

```json
[{ "name": "Espresso Machine X200", "brand": "Acme", "gtin": "4006381333931", "price": 249.9, "priceCurrency": "EUR", "availability": "InStock", "ratingValue": 4.4, "reviewCount": 312, "changedSinceLastRun": true }]
```

The dataset goes to Google Sheets through the Apify integration. A conditional format flags price drops, and a Slack alert fires when a product goes `OutOfStock`.

#### 2. Recipe database for a food app

A product team builds a recipe catalogue for a meal-planning app from 10,000 recipe URLs found in sitemaps: $10 for the whole batch, with ingredients and steps already split into arrays.

```json
{ "urls": ["https://www.allrecipes.com/recipe/16354/easy-meatloaf/", "..."], "schemaTypes": ["Recipe"] }
```

```json
[{ "name": "Easy Meatloaf", "totalTime": "PT70M", "recipeYield": "8", "calories": "372 kcal", "recipeIngredients": ["1 ½ pounds ground beef", "..."], "recipeInstructions": ["Preheat the oven...", "..."], "ratingValue": 4.6 }]
```

Rows are loaded into Postgres. The `markdown` field is embedded as-is into a vector store, so the app's assistant can answer "what can I cook in 30 minutes with ground beef?".

#### 3. Events aggregator for a city guide

An editor collects concerts, exhibitions and meetups from 300 venue and ticketing pages every week (about $0.30 per run). `Event` also matches `MusicEvent`, `TheaterEvent`, `ExhibitionEvent`...

```json
{ "urls": ["https://venue.example.org/agenda", "..."], "schemaTypes": ["Event"] }
```

```json
[{ "primaryType": "MusicEvent", "name": "Jazz Night", "startDate": "2026-11-14T20:30", "locationName": "Le Petit Club", "address": "12 rue X, 31000, Toulouse, FR", "price": 15, "priceCurrency": "EUR", "eventStatus": "EventScheduled" }]
```

A Make scenario publishes new rows to the guide's CMS and a weekly newsletter.

#### 4. Job board enrichment for a recruiting startup

A recruiting startup pulls `JobPosting` markup from 5,000 company career pages: title, company, location, remote flag and salary range for $5.

```json
{ "urls": ["https://careers.example.com/jobs/data-engineer-1234", "..."], "schemaTypes": ["JobPosting"] }
```

```json
[{ "name": "Data Engineer", "hiringOrganization": "Example SA", "jobLocation": "Paris, FR", "jobLocationType": "TELECOMMUTE", "employmentType": "FULL_TIME", "salaryMin": 55000, "salaryMax": 70000, "salaryCurrency": "EUR", "salaryUnit": "YEAR", "datePosted": "2026-10-20" }]
```

#### 5. SEO check of rich-result markup

An SEO consultant checks 500 key URLs of a client site after a redesign. `schemaTypes` and `syntaxesFound` show at a glance which templates lost their Product or Breadcrumb markup, and `noStructuredData` rows list the broken pages, for free.

### API usage

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<USERNAME>/structured-data-extractor").call(run_input={
    "urls": ["https://www.imdb.com/title/tt0111161/"],
    "schemaTypes": ["Movie"],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["name"], item["ratingValue"], item["genre"])
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('<USERNAME>/structured-data-extractor').call({
    urls: ['https://www.allrecipes.com/recipe/16354/easy-meatloaf/'],
    schemaTypes: ['Recipe'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].recipeIngredients);
```

**One HTTP call** (run and get the results synchronously):

```bash
curl -X POST "https://api.apify.com/v2/acts/<USERNAME>~structured-data-extractor/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls":["https://www.imdb.com/title/tt0111161/"]}'
```

### Integrations (Make, Zapier, n8n, Google Sheets)

- **n8n**: *Schedule Trigger* (daily) → *Apify* node "Run actor" with `{"urls": [...], "schemaTypes": ["Product"], "onlyNew": true}` → *Get dataset items* → *IF* `availability == "OutOfStock"` → *Slack* message.
- **Make**: *Apify > Run an Actor* → *Apify > Get Dataset Items* → *Google Sheets > Add a Row* (map `name`, `price`, `priceCurrency`, `availability`, `url`).
- **Zapier**: "Apify: Actor Run Finished" trigger → "Google Sheets: Create Spreadsheet Row".
- **Google Sheets / Excel / CSV**: export the dataset (Overview view) straight from the run page.
- Webhooks, Airbyte, Keboola and the other Apify integrations work as well.

### Use with AI agents (MCP)

The Actor is ready for the Apify MCP server, so Claude, Cursor, ChatGPT and other agents can call it as a tool: "get the price and rating of these 20 product pages", "list the ingredients of this recipe", "what events does this venue announce next month?".

Add `https://mcp.apify.com/?actors=<USERNAME>/structured-data-extractor` to your MCP client. Every result contains a short `markdown` summary, so the agent can read it directly without parsing nested JSON-LD.

### FAQ

**Does it render JavaScript?**
No. It reads the raw HTML, which is where almost every site puts its JSON-LD and OpenGraph tags, because search engines and social networks read them there. Markup injected later by JavaScript (some SPAs, some tag-manager setups) is not seen. Those pages come back as `noStructuredData` and are free.

**What is the "main entity"?**
With `schemaTypes`, it is the first entity of the first requested type. Without a filter, it is the most specific entity on the page (a Product, Recipe or Event rather than WebSite, BreadcrumbList or Organization). All the other entities are still in `entities`.

**Are numbers normalised?**
Prices, ratings, counts, salaries and coordinates are converted to numbers (`"1.234,50"` → `1234.5`). Dates and ISO 8601 durations (`PT1H30M`) are kept exactly as published.

**What about blocked websites?**
If a site refuses the server IP, the page is retried once through a datacenter proxy. CAPTCHAs and anti-bot challenges are never bypassed: those pages are reported as `blocked` and not charged.

**Can I extract every product of a category page?**
Yes, if the page lists them in its markup (e.g. an `ItemList` of `Product`): use `"schemaTypes": ["Product"]` and read the `entities` array. To discover the URLs first, pair it with a sitemap extractor.

**Is it legal?**
The Actor only reads public pages, without login, and only the markup the websites publish for machines. You are responsible for how you use the data (copyright, terms of the source websites, personal data rules).

### Limits

- Raw HTML only (no JavaScript rendering). Pages above 3 MB are cut at 3 MB.
- RDFa support covers RDFa Lite (`vocab`, `typeof`, `property`, `resource`), which is what schema.org markup uses. Exotic full-RDFa features (CURIE prefix maps, `rel`/`rev` chaining) are not interpreted.
- Websites behind CAPTCHA or JavaScript challenges (some large retailers, sometimes IMDb from datacenter IPs) are reported as `blocked`, for free.
- The data is what the website publishes: when a site's markup is wrong or outdated (e.g. a stale price), the output is too.
- Up to 50 entities per page by default (`maxEntitiesPerPage`, max 500).

# Actor input Schema

## `urls` (type: `array`):

Full URLs of the pages to parse (product pages, recipes, events, job offers, articles...). Duplicates and invalid URLs are skipped for free. A bare domain like example.com is accepted.

## `schemaTypes` (type: `array`):

Keep only entities of these schema.org types, nested ones included (e.g. a Product inside an ItemList). Usual subtypes are matched too: Event matches MusicEvent, Article matches NewsArticle and BlogPosting, LocalBusiness matches Restaurant... Pages without a matching entity are free. Leave empty to get everything.

## `syntaxes` (type: `array`):

Structured data formats to read. All by default.

## `onlyNew` (type: `boolean`):

For scheduled runs: remembers a fingerprint of each page's structured data (named key-value store 'structured-data-extractor-state') and only returns and charges pages that are new or whose data changed since the last run (price, availability, rating, dates...).

## `maxEntitiesPerPage` (type: `integer`):

Maximum number of schema.org entities kept in the 'entities' array of each page.

## `proxyFallback` (type: `boolean`):

If a website refuses the server IP (HTTP 403/429), retry that page once through the Apify datacenter proxy. CAPTCHAs and anti-bot challenges are never bypassed.

## `maxConcurrency` (type: `integer`):

Number of pages downloaded at the same time.

## `requestTimeoutSecs` (type: `integer`):

Maximum time to download one page.

## Actor input object example

```json
{
  "urls": [
    "https://www.imdb.com/title/tt0111161/",
    "https://www.allrecipes.com/",
    "https://en.wikipedia.org/wiki/Python_(programming_language)"
  ],
  "schemaTypes": [],
  "syntaxes": [
    "json-ld",
    "microdata",
    "rdfa",
    "opengraph",
    "twitter"
  ],
  "onlyNew": false,
  "maxEntitiesPerPage": 50,
  "proxyFallback": true,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 20
}
```

# Actor output Schema

## `results` (type: `string`):

One item per page with flattened fields, the main entity, all entities, OpenGraph and Twitter Cards (dataset).

## `overview` (type: `string`):

Main columns: URL, status, types, name, price, rating, dates.

## `entities` (type: `string`):

No description

## `runPage` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://www.imdb.com/title/tt0111161/",
        "https://www.allrecipes.com/",
        "https://en.wikipedia.org/wiki/Python_(programming_language)"
    ],
    "schemaTypes": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("actose/structured-data-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": [
        "https://www.imdb.com/title/tt0111161/",
        "https://www.allrecipes.com/",
        "https://en.wikipedia.org/wiki/Python_(programming_language)",
    ],
    "schemaTypes": [],
}

# Run the Actor and wait for it to finish
run = client.actor("actose/structured-data-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.imdb.com/title/tt0111161/",
    "https://www.allrecipes.com/",
    "https://en.wikipedia.org/wiki/Python_(programming_language)"
  ],
  "schemaTypes": []
}' |
apify call actose/structured-data-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,actose/structured-data-extractor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/9yz5qNLdgBDCiFGM9/builds/9SROSqQdYmVBPACTm/openapi.json
