# Structured Data Entity Export (`sapph1re/structured-data-entity-export`) Actor

Export inline JSON-LD entities with raw objects, lexical type filters and exact page, script and JSON-pointer provenance.

- **URL**: https://apify.com/sapph1re/structured-data-entity-export.md
- **Developed by:** [Roman V](https://apify.com/sapph1re) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 entity occurrences

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Structured Data Entity Export

Export inline JSON-LD from a small list of public HTML pages into a dataset of entity occurrences. Select types, inspect the original parsed object and trace each row to its page, script and JSON pointer.

### Quick start

```json
{
  "pageUrls": ["https://www.python.org/"],
  "schemaTypes": [],
  "maxEntities": 20
}
```

Omitted input, API no-body input and `{}` use these same defaults. `{"pageUrls":[]}` is an intentional empty run with no page requests. Null fields and a top-level array are invalid. The saved Python.org control contained one WebSite entity on September 9, 2026; public pages can change.

For a type-filtered example:

```json
{
  "pageUrls": ["https://www.apple.com/"],
  "schemaTypes": ["WebSite"],
  "maxEntities": 5
}
```

Use pages you are authorized to retrieve. The Actor accepts at most five HTTPS URLs on port 443. It fetches static HTML directly, without a browser, login, cookies, proxy, CAPTCHA bypass or custom code. It does not crawl linked pages or request remote JSON-LD contexts.

### Which objects become rows

Inline `<script type="application/ld+json">` blocks are visited in page order. Media type matching ignores case and accepts parameters. The parser selects a root object, objects in a root array, and objects reached through `@graph` containers. Structural arrays are visited in order. Ordinary nested property objects stay inside their parent and never become additional rows.

An object containing `@graph` is treated as a container and never emitted itself. If it also has properties besides `@context` and `@graph`, the page receives `GRAPH_CONTAINER_PROPERTIES_NOT_EXPORTED`; its graph children still survive. Empty graph arrays produce no rows. Scalar array entries, invalid graphs, empty objects and context-only objects receive diagnostics.

`schemaTypes` matches literal, case-sensitive type values. `Article`, `https://schema.org/Article` and `http://schema.org/Article` match each other. Other IRIs, aliases, compact prefixes and subtypes are not inferred. A context that redefines `Article` does not change lexical matching. A match is not proof of Schema.org semantics.

### Dataset fields

Each row contains:

- `raw`: the parsed entity object, with its nested structure and accepted JSON values preserved.
- `types`: string values observed in `@type`; unsupported shapes receive a diagnostic and remain available in `raw`.
- `entityId`, `name`, `headline`, `description`, `url`: values copied directly from the corresponding property (`entityId` uses `@id`). No fallback, coercion or inference. Missing properties map to null in these common fields; `raw` distinguishes absence from explicit null.
- `provenance`: zero-based `pageIndex`, zero-based `blockIndex` among JSON-LD scripts, zero-based `scriptIndex` among all scripts, one-based source line, zero-based source column, original normalized request URL, final URL and RFC 6901 `jsonPointer`. The root pointer is the empty string. `/1/@graph/0` identifies the first graph object inside the second root-array element.
- `occurrenceId`: a SHA-256 identifier derived from that provenance. It is repeatable for unchanged source location, but can change when source layout or input order changes.
- `contentHash`: a SHA-256 hash of the key-sorted parsed object, without provenance or retrieval time. It identifies identical accepted content, not a verified real-world entity.
- `retrievedAt`: UTC time when this page's parsed rows were produced.

Repeated objects and duplicate or missing `@id` values are preserved as separate occurrences. Conflicting records are never merged. Identical normalized input URLs are fetched once; later input entries receive `DUPLICATE_INPUT` with the first index. Query strings remain significant.

Relative URLs and IDs remain exactly lexical. The Actor does not apply HTML `<base>`, JSON-LD `@base`, vocabulary expansion or URL resolution to source properties. Reported URLs are source values and may be unsafe or inaccessible; the Actor does not follow them.

`raw` preserves JSON values, not original whitespace, escape spelling or numeric token formatting. Duplicate object keys, non-finite values, lone Unicode surrogates, integers outside the interoperable ±(2^53-1) range and decimals that would lose value in serialization are rejected at block level. Malformed JSON is never repaired. HTML entities inside script text are not decoded a second time. JSON-LD uses the HTTP-declared charset, or strict UTF-8 when absent; unsupported encodings fail explicitly.

### Outcomes and diagnostics

Only selected entity rows enter the default dataset. The `OUTPUT` key-value record contains per-page outcomes and block diagnostics, plus source request counters and delivery receipts.

| Status | Meaning |
| --- | --- |
| `USEFUL` | Selected rows were delivered, with no reported omissions or failures. |
| `EMPTY` | The accepted pages had no matching entities, or the explicit page list was empty. |
| `PARTIAL` | Useful rows survived page/block failures, or a declared source/row/budget cap omitted work. |
| `INVALID` | Runtime input validation failed before source access. Platform schema validation may reject the request before a run starts. |
| `FAILED` | No useful row survived a source/parse failure, or delivery, receipt or persistence failed. |

A malformed block receives its exact block/script location and JSON error line, column and character offset when available. Valid sibling blocks remain eligible. Diagnostics are capped and explicitly mark truncation. An HTTP error is a source failure; it is never reported as an empty schema page. `INVALID` and `FAILED` return nonzero process exit codes. Read the structured outcome even when a PARTIAL run exits successfully.

`billing.deliveryStatus` is `NOT_ATTEMPTED`, `CONFIRMED` or `UNKNOWN`. A failed or malformed append receipt produces null counts when delivery cannot be established. A later bookkeeping or OUTPUT-save failure preserves counts already confirmed. The delivery journal guards against duplicate appends: a completed journal returns its saved receipt, while an incomplete or malformed journal stops without retrying the append. These replay paths are covered by local regression tests; hosted recovery has not been demonstrated. Keep platform automatic restart disabled.

### Limits

| Resource | Hard maximum |
| --- | --- |
| Input pages | 5, processed sequentially |
| Entity rows | 200 total; default 20 |
| Source HTTP attempts | 20 across the run, including redirects and failed connections |
| Redirects | 3 per page; every target and DNS answer is checked |
| Page fetch time | 12 seconds total; 7-second connect/read limits |
| Source collection time | 60 seconds, with parsing deadline checks |
| Response body | 2 MiB encoded, 4 MiB decoded per successful page |
| Compression | gzip (up to 16 members), zlib deflate or Brotli; complete consumption required |
| JSON-LD blocks | 64 per page, 256 KiB UTF-8 per block |
| JSON structure | Depth 32; 20,000 nodes including keys per block |
| Dataset row and append | 128 KiB per row; 4 MiB for the batch |
| Diagnostics | At most 257 entries per page, including cap markers |

TLS certificate verification remains enabled. DNS results must all be public; private, reserved, loopback, link-local, multicast and transition addresses are rejected. Environment proxies and cookies are disabled. Redirects do not forward credentials; credential-bearing input URLs are rejected. Responses require `text/html` or `application/xhtml+xml`. Compressed trailing corruption, truncation and decoded-size overflow fail explicitly. The source client and dataset POST client disable their pinned library retry paths. Request counts describe source fetch attempts, not DNS queries, TCP packets or Apify storage operations.

The byte caps describe response body bytes, not TLS or HTTP framing. Header field/count limits are also set. Parsing is bounded by input size, depth and nodes; wall-clock checks occur between parser chunks and blocks. Platform termination remains the outer run-time boundary.

### Pricing

The price is $0.001 per delivered entity occurrence ($1 per 1,000), charged through the automatic `apify-default-dataset-item` event. There is no custom start, page or report event. Empty results and diagnostic records do not create entity events.

The effective row cap is the smaller of `maxEntities`, the SDK's remaining capacity and an exact decimal calculation from the configured event budget. At $0.001 per row, a $0.0025 event cap permits two rows. Do not use a literal zero API maximum as a no-spend control: the platform can ignore it, and positive maxima below one event may be rejected before a run starts. An explicit empty `pageUrls` list performs no page requests. Selected rows are appended once in a batch. An uncertain POST is never automatically retried. No claim of actual charge or delivery is made from local row selection alone.

The $0.001 entity price includes platform usage for customer runs. The event cap is not an account-wide spending ceiling. Developer/owner QA and retained-storage accounting are tracked separately, not added as another per-entity customer fee.

### Scope and local development

This is inline extraction, without RDF expansion, SEO certification, truth verification, authentication, enrichment, people profiling or job-board specialization. It does not inspect markup inserted by JavaScript. Source availability, authorization and content can change. Use the raw object and provenance when evaluating the source's claims.

Run `python -m src` in the Actor directory after installing the hash-locked requirements in a fresh Python 3.12 environment. `python run_tests.py` runs the Actor-local suite; its SDK integration tests also need the matching benchmark fixtures and `jsonschema==4.26.0`. Deployment uses the included Dockerfile.

# Actor input Schema

## `pageUrls` (type: `array`):

Up to five exact authorized HTTPS URLs, port 443 only. Omission uses Python.org. An explicit empty array performs no source requests.

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

Optional case-sensitive lexical types such as Article or https://schema.org/Article. No context expansion or subtype inference. Empty selects all eligible objects.

## `maxEntities` (type: `integer`):

Total selected occurrences across all pages, before one dataset append. A lower event budget may reduce this cap.

## Actor input object example

```json
{
  "pageUrls": [
    "https://www.python.org/"
  ],
  "schemaTypes": [],
  "maxEntities": 20
}
```

# Actor output Schema

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

No description

## `outcomes` (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 = {
    "pageUrls": [
        "https://www.python.org/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sapph1re/structured-data-entity-export").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 = { "pageUrls": ["https://www.python.org/"] }

# Run the Actor and wait for it to finish
run = client.actor("sapph1re/structured-data-entity-export").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 '{
  "pageUrls": [
    "https://www.python.org/"
  ]
}' |
apify call sapph1re/structured-data-entity-export --silent --output-dataset

```

## MCP server setup

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

```

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/BlEmGGYyUdg5LADc9/builds/p0OBbktcvaM1MNe0Q/openapi.json
