# AI Document Summarizer and Field Extractor (`sorrel_flint/ai-doc-extract`) Actor

Summarize web pages and text and extract typed fields like prices, dates and skills as JSON, using your own OpenAI-compatible API key.

- **URL**: https://apify.com/sorrel_flint/ai-doc-extract.md
- **Developed by:** [Stasis Lagunas](https://apify.com/sorrel_flint) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 94.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 document processed with your own api keys

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?

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

## AI Document Summarizer and Field Extractor

**Turn public web pages (HTML, no JavaScript rendering) or plain text into a short summary plus
the exact fields you name, as typed JSON rows.** Point it at job ads, product pages, press releases
or policies, list the fields you want (price, salary, deadline, skills, anything), and get one clean
row per document, ready for a spreadsheet, a database or your app.

**You bring the model:** your own OpenAI key, or a key for OpenRouter or any other
OpenAI-compatible service. Your provider bills you for the model; this Actor charges 0.01 USD per
processed document plus 0.005 USD per run. **No key yet? Just press Start:** without a key the run
is a preview of the first 3 documents with placeholder values, which costs only the 0.005 USD start fee.

### What people use it for

- **Job ads:** salary, location, remote or not, required skills and the application deadline.
- **Product pages:** price, currency, stock status, brand and key specifications.
- **Press releases and news:** company, date, people mentioned and a two-sentence summary.
- **Policies and terms:** retention periods, notice periods, fees and the clauses you care about.
- **Any list of pages you already have:** one row per page with the same fields, so pages from
  different sites become comparable.

### How to use it

1. Add the pages under **Page URLs**, or paste text under **Inline documents**.
2. Under **Fields to extract**, name each field, pick its type (`string`, `number`, `boolean` or
   `string[]`) and say in a few words what it means.
3. Add your API key under **Model and API key** and press Start. Or press Start without a key
   first: the preview shows the exact output format on your own pages for the start fee only.

### Example input

```json
{
    "openaiApiKey": "sk-...",
    "model": "gpt-4o-mini",
    "urls": ["https://example.com/"],
    "fields": [
        { "name": "company", "type": "string", "description": "who published the page" },
        { "name": "salary", "type": "number", "description": "monthly salary in DKK" },
        { "name": "remote", "type": "boolean" },
        { "name": "skills", "type": "string[]", "description": "required skills" }
    ],
    "summaryLength": "short",
    "outputLanguage": "English"
}
```

For OpenRouter or another OpenAI-compatible service, also set `openaiBaseUrl` (for example
`https://openrouter.ai/api/v1`) and use that service's model name (for example `openai/gpt-4o-mini`).

### Example output

**Illustration of the output format.** The values below are made up to show the shape of a row for
the example input above (a job ad); they are not from a real run.

```json
{
    "name": "Junior Python Developer - Example ApS",
    "source": "url",
    "url": "https://jobs.example.com/junior-python-developer",
    "title": "Junior Python Developer - Example ApS",
    "status": "ok",
    "summary": "Example ApS is hiring a junior Python developer in Aarhus to build internal data tools. The role uses Python and SQL, and applications close on 31 October.",
    "fields": {
        "company": "Example ApS",
        "salary": 42000,
        "remote": false,
        "skills": ["Python", "SQL"]
    },
    "model": "gpt-4o-mini",
    "charged": true,
    "parts": 1,
    "truncated": false,
    "usage": { "promptTokens": 2314, "completionTokens": 187 }
}
```

**A real preview row.** This is what a run without a key returned on 2026-10-02 for the default
input (example.com and two fields), shortened: placeholder values, not charged.

```json
{
    "name": "Example Domain",
    "url": "https://example.com/",
    "model": "test-mode",
    "summary": "[test mode] 27 words. Starts with: This domain is for use in documentation examples without needing permission. ...",
    "fields": { "company": "[test mode] company", "topics": [] },
    "charged": false
}
```

### What you get per document

| Field | Meaning |
|---|---|
| `name`, `url`, `title` | Where the document came from |
| `summary` | 2, 5 or 12 sentences, in the document's language or the one you choose |
| `fields` | Your fields, typed: `string`, `number`, `boolean` or `string[]`; `null` when not stated |
| `status` | always `ok`: skipped and failed documents are not in the dataset |
| `charged` | Whether this row was billed |
| `parts`, `truncated`, `chars`, `usage` | How the document was processed and the model tokens used |

Numbers are normalized: `1.234,50 kr`, `1,234.50` and `EUR 1 200` all come back as plain numbers.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Actor start | 0.005 USD per run |
| Document processed | 0.01 USD per document |

- **The model is billed by your provider, not by this Actor.** Set `openaiApiKey` to your OpenAI
  key, or to a key for an OpenAI-compatible service plus its `openaiBaseUrl`.
- **No key, preview:** without a key the Actor runs in test mode on the first 3 documents, with
  placeholder values instead of a model call, and says so in the status message. It costs only the
  0.005 USD start fee. If none of those pages can be fetched, it shows one built-in sample instead.
- You only pay for documents that were processed successfully. Skipped and failed documents are free:
  they are listed with the reason in the `ISSUES` record of the run's key-value store, not in the dataset.
- The run stops starting new documents when your maximum charge for the run is reached, and after
  5 documents in a row fail at the model (for example a wrong API key).
- **Test mode** works the same way on up to 10 documents, also with a key present. Use it to check
  your field definitions before a paid run.

### FAQ

**Do I need an API key?** For real summaries and fields, yes: an OpenAI key, or a key for an
OpenAI-compatible service (OpenRouter and others) together with its `openaiBaseUrl`. Without a key
you get the preview described above.

**Which models work?** Any chat model your key can use that can answer in JSON. With OpenAI,
`gpt-4o-mini` is fast and cheap and `gpt-4o` is stronger. With OpenRouter, use its model ids, for
example `openai/gpt-4o-mini`.

**What does a run cost?** Here: 0.005 USD per run plus 0.01 USD per document processed, so 100
pages cost 1.005 USD on Apify. Your provider bills the model separately; at OpenAI's list price for
`gpt-4o-mini` when this was written (0.15 USD per million input tokens), a 10,000-character page is
well under 0.001 USD. Set a maximum charge for the run and the Actor stops starting documents when
it is reached.

**Am I charged for pages that fail?** No. Skipped pages (robots.txt, not found, not HTML, private
addresses) and model failures are free and listed with the reason in the run's `ISSUES` record.

**Can it read PDFs, JavaScript-heavy sites or whole websites?** Not yet: one HTML or plain-text page
per URL, read without running JavaScript, and links are not followed. For a page that needs
JavaScript, paste its text as an inline document.

**Is my API key safe?** It is a secret input: Apify stores it encrypted, and the Actor never writes
it to the output or the log. It is only sent to the model endpoint you choose, which must be a
public address.

**Can I call it from my own code or an automation tool?** Yes: like every Apify Actor it runs from
the Apify API, the Python and JavaScript clients, and Apify's integrations (Make, Zapier, n8n and
others). The API tab on this page shows a ready example.

### Good to know

- One page per URL; the Actor does not crawl links. HTML and plain-text pages only (no PDF yet).
- Pages are read as served, without running JavaScript: text that only appears after scripts run
  (many single-page apps) is not seen. Paste such text as an inline document instead.
- robots.txt is respected, on every redirect too. Pages it disallows are skipped and not charged.
- Only public addresses are fetched: local, private and cloud-metadata addresses are refused, for
  pages, redirects and your own model endpoint alike.
- Long documents are processed in parts of about 12,000 tokens (roughly 36,000 characters of English
  text, fewer for Chinese or Japanese) and the part results are merged. Text beyond "Max characters
  per document" (default 40,000, up to 400,000) is cut off, and the row says `truncated: true`.
  Longer documents mean more model calls on your provider's bill.
- Limits per run: at most 1,000 documents, 30 fields (descriptions up to 120 characters) and
  instructions up to 500 characters.
- The model must be able to answer in JSON; most current chat models can. Summary quality and
  languages are the model's: the summary follows the document's language unless you set one.
- Text inside the document is treated as data: the model is told to ignore instructions hidden in a page.
- Your API key is a secret input: it is stored encrypted by Apify and never written to the output or log.

# Changelog

This Actor's version history is a separate document: https://apify.com/sorrel_flint/ai-doc-extract/changelog.md

# Actor input Schema

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

Web pages to summarize, one document per URL (no crawling). robots.txt is respected and only public addresses are fetched; skipped URLs are listed in the ISSUES record and never charged.

## `documents` (type: `array`):

Documents supplied as text instead of URLs. Each item is an object with a 'name' and a 'text' field.

## `fields` (type: `array`):

Structured fields to pull out of every document, at most 30. Each item is an object: {"name": "price", "type": "number", "description": "asking price in EUR"} (description at most 120 characters). Types: string, number, boolean, string\[]. Leave empty to get only a summary.

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

What the summary should focus on (at most 500 characters). Leave empty for a neutral summary of the whole document.

## `summaryLength` (type: `string`):

How long the summary should be.

## `outputLanguage` (type: `string`):

Language of the summary, for example 'English' or 'Danish'. Leave empty to keep the language of the document.

## `openaiApiKey` (type: `string`):

Your OpenAI key, or a key for any OpenAI-compatible service (set its URL below). Your provider bills you for the model directly; this Actor charges 0.01 USD per processed document. Without a key the run is a preview of the first 3 documents with placeholder values, which costs only the 0.005 USD start fee. Stored encrypted, never written to the output or log.

## `model` (type: `string`):

A model your key can use and that can answer in JSON. OpenAI: gpt-4o-mini (fast, cheap) or gpt-4o. OpenRouter: its ids, for example openai/gpt-4o-mini.

## `openaiBaseUrl` (type: `string`):

Leave empty for OpenAI. For another OpenAI-compatible service, its Chat Completions base URL, for example https://openrouter.ai/api/v1.

## `maxCharsPerDocument` (type: `integer`):

Text beyond this many characters is cut off (the row says truncated: true). Long documents are processed in parts and the part results are merged; longer documents mean more model calls on your provider's bill.

## `testMode` (type: `boolean`):

Run the whole pipeline on up to 10 documents with placeholder values instead of a model call; no document is charged. Use it to check your field definitions first. Runs without a key are a preview like this automatically (first 3 documents).

## Actor input object example

```json
{
  "urls": [
    "https://example.com/"
  ],
  "documents": [],
  "fields": [
    {
      "name": "company",
      "type": "string",
      "description": "who published the page"
    },
    {
      "name": "topics",
      "type": "string[]",
      "description": "main topics of the page"
    }
  ],
  "instructions": "",
  "summaryLength": "medium",
  "outputLanguage": "",
  "model": "gpt-4o-mini",
  "openaiBaseUrl": "",
  "maxCharsPerDocument": 40000,
  "testMode": false
}
```

# Actor output Schema

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

No description

## `issues` (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://example.com/"
    ],
    "documents": [],
    "fields": [
        {
            "name": "company",
            "type": "string",
            "description": "who published the page"
        },
        {
            "name": "topics",
            "type": "string[]",
            "description": "main topics of the page"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sorrel_flint/ai-doc-extract").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://example.com/"],
    "documents": [],
    "fields": [
        {
            "name": "company",
            "type": "string",
            "description": "who published the page",
        },
        {
            "name": "topics",
            "type": "string[]",
            "description": "main topics of the page",
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("sorrel_flint/ai-doc-extract").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://example.com/"
  ],
  "documents": [],
  "fields": [
    {
      "name": "company",
      "type": "string",
      "description": "who published the page"
    },
    {
      "name": "topics",
      "type": "string[]",
      "description": "main topics of the page"
    }
  ]
}' |
apify call sorrel_flint/ai-doc-extract --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sorrel_flint/ai-doc-extract"
        }
    }
}
```

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/Zj0jrxNXfQhf5xUMe/builds/MoI8Tt9tae4S6xZdN/openapi.json
