# Page Quarry (`resultquarry/page-quarry`) Actor

Extract structured JSON from ordinary public webpages.

- **URL**: https://apify.com/resultquarry/page-quarry.md
- **Developed by:** [Result Quarry](https://apify.com/resultquarry) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 results

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

## Page Quarry

Turn ordinary public webpages into structured JSON with natural-language instructions and an
optional JSON Schema.

Page Quarry fetches each URL, reads the content present in its initial HTML, extracts the requested
facts with Gemini 3.5 Flash-Lite, validates the response, and writes one auditable record per URL to
the run's default Dataset.

### What you can extract

Use Page Quarry with public, server-rendered pages such as:

- articles and documentation;
- product or detail pages;
- tables;
- lists and card layouts;
- long text pages;
- pages with mildly malformed HTML or legacy text encodings.

Example requests include “extract the product name, price, and availability,” “return every row in
this table,” or “find the policy title, effective date, and covered regions.”

### Quick start

Provide one to ten public URLs and describe the result you want. A JSON Schema is optional but
recommended when downstream code expects stable fields and types.

```json
{
  "startUrls": [
    {"url": "https://example.com/"}
  ],
  "instructions": "Extract the page title and its main descriptive sentence.",
  "jsonSchema": {
    "type": "object",
    "properties": {
      "title": {"type": "string"},
      "description": {"type": "string"}
    },
    "required": ["title", "description"],
    "additionalProperties": false
  }
}
```

Without `jsonSchema`, Page Quarry uses JSON-output mode and still requires the model response to
parse as JSON. It does not generate a schema for you.

### Worked example

For the input above, the Dataset record is:

```json
{
  "url": "https://example.com/",
  "finalUrl": "https://example.com/",
  "status": "success",
  "extracted": {
    "title": "Example Domain",
    "description": "This domain is for use in documentation examples without needing permission."
  },
  "schemaValid": true,
  "httpStatus": 200,
  "provider": "google-gemini",
  "model": "gemini-3.5-flash-lite",
  "timing": {
    "fetchSeconds": 0.051963,
    "transformSeconds": 0.002634,
    "inferenceSeconds": 8.291204,
    "totalSeconds": 8.346644
  },
  "usage": {
    "inputTokens": 159,
    "outputTokens": 30,
    "thinkingTokens": 0,
    "totalTokens": 189
  },
  "retries": {
    "fetch": 0,
    "provider": 0,
    "output": 0
  },
  "responseBytes": 559,
  "markdownCharacters": 184,
  "redirects": []
}
```

This one-result run costs:

`(1 result × $0.015) + (1 run × $0.00005) = $0.01505`

A ten-URL run that emits ten records costs `$0.15005`. The result rate is `$15 per 1,000 Dataset
results`, plus the small per-run start charge.

### Input reference

| Field | Required | Default | Description |
|---|---:|---:|---|
| `startUrls` | Yes | — | One to ten public HTTP/HTTPS URL objects in Apify's standard `{ "url": "…" }` form. |
| `instructions` | Yes | — | What to extract, up to 10,000 characters. |
| `jsonSchema` | No | JSON mode | JSON Schema used for structured output and local validation. |
| `maxPageBytes` | No | 10 MiB | Maximum response body size. Configurable from 1 byte to 25 MiB. |
| `fetchTimeoutSecs` | No | 30 | Per-request fetch timeout, from 1 to 60 seconds. |

Compatible schemas can use object, array, and scalar types; properties and required fields;
`additionalProperties`; enums and formats; numeric bounds; and array item/count constraints.
Unsupported keywords such as `$ref` and `$defs` are rejected before page fetching begins.

### Output reference

The default Dataset contains one record per processed URL. A record has `status: "success"` with an
`extracted` value, or `status: "error"` with a stable `errorCode` and `errorCategory`. Full source
HTML, extraction instructions, schemas, and raw model responses are not included in the Dataset or
ordinary logs.

| Field | Description |
|---|---|
| `url` | Original URL supplied in the input. |
| `finalUrl` | URL after accepted public redirects, when fetching reached a page. |
| `status` | `success` or `error`. |
| `extracted` | Structured JSON returned by the model after validation. Present on success. |
| `errorCode` | Stable machine-readable failure code, such as `http_404`, `unusable_content`, or `provider_timeout`. |
| `errorCategory` | Broad failure stage: `fetch`, `content`, `provider`, or `validation`. |
| `httpStatus` | Final HTTP status when available. |
| `provider` / `model` | Inference backend used for the record. |
| `schemaValid` | Whether output passed JSON parsing and the supplied schema. On schema-less calls this means valid JSON. |
| `timing` | Fetch, transformation, inference, and total pipeline seconds. |
| `usage` | Provider-reported input, output, thinking, and total tokens. |
| `retries` | Counts of fetch, provider-transport, and invalid-output retries. |
| `responseBytes` | Downloaded response size. |
| `markdownCharacters` | Size of the Markdown used for extraction. |
| `redirects` | Accepted redirect chain. Every destination is checked against the public-network policy. |
| `fetchAttempts` / `providerAttempts` | Sanitized attempt telemetry with status, reason, latency, and bounded retry delay. |

Each emitted Dataset record is a billable result, including a classified failure record. Failed
pages still require fetch or provider work, and the error record gives you an auditable outcome
instead of silently dropping the URL. Input rejected before URL processing produces no Dataset
item; the small Actor-start event still applies.

### Failure handling

Page Quarry retries only failures that are detectable and likely transient:

- fetch network errors, timeouts, HTTP 408/425/429, and selected 5xx responses;
- provider network errors, timeouts, HTTP 408/429, and 5xx responses;
- one additional identical inference request after empty, malformed, or schema-invalid output.

Transport attempts are bounded at three. `Retry-After` is honored up to 60 seconds. Page Quarry does
not retry because extracted values merely appear semantically wrong, and it never sends a repair or
self-critique prompt.

### Supported and unsupported pages

Page Quarry is intentionally HTTP-only. It does not support:

- authenticated pages, cookies, bearer tokens, or custom authorization headers;
- localhost, private, loopback, link-local, reserved, or cloud-metadata destinations;
- browser rendering or content that appears only after JavaScript executes;
- CAPTCHA solving, anti-bot evasion, or access-denied pages;
- PDFs and other non-HTML documents;
- crawling, link discovery, or agentic navigation.

Every redirect is rechecked against the same public-network policy. Webpage content is treated as
untrusted data and cannot grant the model tools, change the extraction request, or authorize an
external action.

### Accuracy and data handling

AI extraction can make semantic mistakes even when JSON is schema-valid. In the 22-case evaluation,
Page Quarry achieved 86.6% field accuracy, 86.1% critical-field accuracy, and 15/22 exact complete
records. Verify important outputs before taking consequential action.

Page contents, extraction instructions, and the supplied schema are sent to Google Gemini for
inference. Do not put secrets or private data in URLs, instructions, or schemas. Page Quarry is for
ordinary public-web content only.

# Actor input Schema

## `startUrls` (type: `array`):

One to ten public HTTP/HTTPS pages. Private networks and authenticated pages are rejected.

## `instructions` (type: `string`):

Describe the facts to return. Page content is treated as untrusted data.

## `jsonSchema` (type: `object`):

A Gemini-compatible JSON Schema for native structured output and local validation.

## `maxPageBytes` (type: `integer`):

Stop downloading a response after this many bytes.

## `fetchTimeoutSecs` (type: `integer`):

Per-request public webpage fetch timeout.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ],
  "instructions": "Extract the page title and its main descriptive sentence.",
  "jsonSchema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string"
      },
      "description": {
        "type": "string"
      }
    },
    "required": [
      "title",
      "description"
    ],
    "additionalProperties": false
  },
  "maxPageBytes": 10485760,
  "fetchTimeoutSecs": 30
}
```

# Actor output Schema

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

One auditable Dataset record per processed URL, containing extracted JSON or a classified error plus timing, token, and retry metadata.

# 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 = {
    "startUrls": [
        {
            "url": "https://example.com/"
        }
    ],
    "instructions": "Extract the page title and its main descriptive sentence.",
    "jsonSchema": {
        "type": "object",
        "properties": {
            "title": {
                "type": "string"
            },
            "description": {
                "type": "string"
            }
        },
        "required": [
            "title",
            "description"
        ],
        "additionalProperties": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("resultquarry/page-quarry").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 = {
    "startUrls": [{ "url": "https://example.com/" }],
    "instructions": "Extract the page title and its main descriptive sentence.",
    "jsonSchema": {
        "type": "object",
        "properties": {
            "title": { "type": "string" },
            "description": { "type": "string" },
        },
        "required": [
            "title",
            "description",
        ],
        "additionalProperties": False,
    },
}

# Run the Actor and wait for it to finish
run = client.actor("resultquarry/page-quarry").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 '{
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ],
  "instructions": "Extract the page title and its main descriptive sentence.",
  "jsonSchema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string"
      },
      "description": {
        "type": "string"
      }
    },
    "required": [
      "title",
      "description"
    ],
    "additionalProperties": false
  }
}' |
apify call resultquarry/page-quarry --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,resultquarry/page-quarry"
        }
    }
}

```

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/c2EdlQE6Z3xtby3iO/builds/ESdkm3g5tbkO4MQ3P/openapi.json
