# PDF Metadata & Content Extractor — Extract text & metadata (`perryay/pdf-metadata-extractor`) Actor

Download and extract metadata, text content, and page count from PDF documents. Supports batch processing with concurrent downloads.

- **URL**: https://apify.com/perryay/pdf-metadata-extractor.md
- **Developed by:** [Perry AY](https://apify.com/perryay) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.015 / actor start

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## PDF Metadata & Content Extractor — Extract Text, Metadata, and Tables from PDF Documents

**Download PDF documents from URLs and extract metadata, text content, and embedded tables.** Whether you're archiving document properties, indexing PDF text for search, or extracting tabular data from reports, this actor handles the heavy lifting in a single API call.

---

### What does it do?

This actor downloads PDF documents from one or more URLs and extracts their metadata (title, author, subject, creator, producer), full text content, embedded tables, and structural information (page count, file size). It uses PyMuPDF for reliable PDF parsing and httpx for concurrent downloads with configurable concurrency.

### Who is it for?

| Persona | What they use it for |
|---------|---------------------|
| Data Engineer | Extracting text and metadata from PDFs at scale for document processing pipelines |
| Content Manager | Batch-indexing PDF documents for search, archiving metadata such as author and subject |
| Legal/Compliance Analyst | Extracting text from contracts, agreements, and regulatory filings for review and audit |
| Researcher | Harvesting metadata and content from academic papers, PDF reports, and whitepapers |
| Document Automation Developer | Building workflows that transform PDF content into structured data for downstream systems |
| Backend Developer | Integrating PDF extraction into CI/CD, ETL, or document management systems without installing native PDF libraries |

### Why use this?

- **Batch processing with concurrent downloads** — Process up to dozens of PDF URLs in a single run. The actor downloads up to 3 documents concurrently.
- **Rich metadata extraction** — Pull title, author, subject, creator, producer, and other standard PDF metadata fields.
- **Full text extraction** — Extract all readable text from every page. Text is returned as a single string (truncated to 10,000 characters).
- **Table detection** — Automatically locate and extract tabular structures embedded in PDF pages.
- **Graceful error handling** — Network timeouts, invalid PDFs, and download failures are caught per-URL so one bad link never takes down the whole batch.
- **No local dependencies needed** — Call it from cURL, Python, or any HTTP client.

### Input Parameters

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `urls` | array of strings | yes | — | Array of PDF URLs to download and process |
| `extractText` | boolean | no | `true` | Extract full text content from every page |
| `extractMetadata` | boolean | no | `true` | Extract document metadata (title, author, subject, etc.) |

#### Example Input

```json
{
  "urls": [
    "https://example.com/document1.pdf",
    "https://example.com/report-2024.pdf"
  ],
  "extractText": true,
  "extractMetadata": true
}
````

### Output Format

Each PDF URL produces one result object in the dataset:

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | The original PDF URL |
| `filename` | string | Derived filename from the URL |
| `pageCount` | integer | Total number of pages in the PDF |
| `fileSize` | integer | Size of the downloaded PDF in bytes |
| `metadata` | object | Document metadata. Empty if `extractMetadata` is `false`. |
| `text` | string or null | Extracted text (truncated to 10,000 chars). `null` if `extractText` is `false`. |
| `tables` | array | Detected tables as arrays of rows. Empty if none found. |
| `error` | string or null | Error message if processing failed. `null` on success. |

#### Example Output

```json
{
  "url": "https://example.com/document1.pdf",
  "filename": "document1.pdf",
  "pageCount": 12,
  "fileSize": 284756,
  "metadata": {
    "title": "Annual Report 2024",
    "author": "Jane Doe",
    "subject": "Financial Results",
    "creator": "Microsoft Word",
    "producer": "Adobe PDF Library 17.0"
  },
  "text": "Annual Report 2024\n\nIntroduction\nThis report summarizes...",
  "tables": [
    [["Quarter", "Revenue", "Expenses"], ["Q1", "$1.2M", "$800K"]]
  ],
  "error": null
}
```

### API Usage

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/perryay~pdf-metadata-extractor/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com/document.pdf"],
    "extractText": true,
    "extractMetadata": true
  }'
```

#### Python (ApifyClient)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("perryay~pdf-metadata-extractor").call(
    run_input={
        "urls": ["https://example.com/document.pdf"],
        "extractText": True,
        "extractMetadata": True,
    }
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"URL: {item['url']}, Pages: {item['pageCount']}")
```

### Use Cases

1. **Document Archiving and Metadata Harvesting** — Automate extraction of PDF metadata across thousands of documents for library cataloging, digital asset management, or compliance archiving.

2. **Search Indexing of PDF Content** — Extract full text from PDF documents and feed it into a search engine (Elasticsearch, Algolia, Meilisearch) for full-text search across your document repository.

3. **Contract and Agreement Analysis** — Process batches of legal contracts, NDAs, and service agreements to extract text for clause analysis, key-term identification, or regulatory review.

4. **Financial Report Data Extraction** — Pull tables from quarterly reports, balance sheets, and income statements for quantitative analysis.

5. **Academic Paper Metadata Collection** — Harvest metadata and abstracts from collections of academic PDFs for research bibliographies and citation management.

6. **Document Quality Assurance** — Validate that generated PDFs have the correct page count, file size, and metadata fields before distribution.

7. **ETL Pipeline Integration** — Use the actor as a transform step in an ETL pipeline: ingest PDF URLs, extract metadata and text, and output structured records to a data warehouse.

### FAQ

**Q: Can I process more than one PDF URL in a single run?**
A: Yes. Pass an array of PDF URLs in the `urls` field. Each URL's result is independent — a failure on one does not affect the others.

**Q: What happens if a PDF URL returns an HTTP error?**
A: The actor catches the HTTP error and returns a result with the `error` field set. The rest of the batch continues uninterrupted.

**Q: How much text content is returned per PDF?**
A: Text is truncated to 10,000 characters by default. This covers the full content of most documents and keeps the output manageable.

**Q: Does the table extraction work on scanned PDFs?**
A: No. PyMuPDF's `find_tables()` works on PDFs with real text and vector graphics. Scanned PDFs that are purely image-based do not contain extractable tables.

**Q: What PDF metadata fields are extracted?**
A: All standard PDF metadata fields: title, author, subject, creator, producer, keywords, and format. Empty fields are omitted.

**Q: Is there a maximum PDF file size?**
A: The actor downloads PDFs up to several hundred megabytes. Timeout per download is 60 seconds with up to 3 retries.

**Q: Can I skip text extraction and only get metadata?**
A: Yes. Set `extractText` to `false` in the input for faster processing.

**Q: Can I skip metadata extraction?**
A: Yes. Set `extractMetadata` to `false`.

**Q: Does the actor follow redirects?**
A: Yes. The HTTP client follows up to 5 redirects to resolve the final PDF URL.

**Q: What if the URL points to an HTML page rather than a PDF?**
A: The actor expects direct PDF URLs. If the URL points to an HTML page containing an embedded PDF viewer, resolve the actual PDF URL first.

**Q: Does the actor charge for failed PDF downloads?**
A: No. Only PDFs that are successfully downloaded, parsed, and processed incur a charge event. Network errors and invalid PDFs are not charged.

### Usage & Billing

This actor uses Apify's PAY\_PER\_EVENT pricing model. You are charged per event:

| Event Name | Price (USD) | Trigger |
|------------|-------------|---------|
| `apify-actor-start` | $0.015 | Charged on every actor start |
| `pdf-extract` | $0.010 | Charged per PDF processed |
| `text-extraction` | $0.005 | Charged when full text is extracted |
| `table-extraction` | $0.005 | Charged when tables are found and extracted |

Platform infrastructure costs (Apify's compute and storage) are passed through at cost.

### MCP Integration

This actor can be used through the [Apify MCP server](https://docs.apify.com/integrations/mcp).
Once connected, your MCP client (Claude Desktop, Cursor, etc.) can discover and run
this actor from the Apify Store.

#### Quick Start

1. **Install the Apify connector** in your MCP client:
   - **Claude Desktop**: Search for "Apify" in the connector directory, or use the remote server at `https://mcp.apify.com`
   - **Other clients**: See the [Apify MCP server docs](https://docs.apify.com/integrations/mcp) for setup instructions

2. **Ask your AI assistant** to use the actor.

#### Claude Desktop Configuration

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com"
    }
  }
}
```

On first connection, your browser opens to sign in to Apify and authorize access.

#### Bearer Token Alternative

For MCP clients that do not support browser-based OAuth (e.g., Cursor, VS Code, Windsurf), use a Bearer token directly:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com",
      "headers": {
        "Authorization": "Bearer <APIFY_TOKEN>"
      }
    }
  }
}
```

### Related Tools

- **[Meta Mate](https://apify.com/perryay/meta-mate)** — Extract Open Graph, Twitter Cards, and JSON-LD metadata from URLs
- **[QR Craft](https://apify.com/perryay/qr-craft)** — Generate high-quality QR codes in PNG or SVG format
- **[UUID Lab](https://apify.com/perryay/uuid-lab)** — Generate UUIDs, nanoids, short IDs, and ULIDs
- **[Domain Intel](https://apify.com/perryay/domain-intel)** — WHOIS lookups, DNS enumeration, and SSL certificate validation

# Actor input Schema

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

Array of PDF URLs to extract metadata and content from

## `extractText` (type: `boolean`):

Extract full text content from each PDF

## `extractMetadata` (type: `boolean`):

Extract document metadata (title, author, subject, etc.)

## Actor input object example

```json
{
  "urls": [
    "https://example.com/sample.pdf"
  ],
  "extractText": true,
  "extractMetadata": true
}
```

# Actor output Schema

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

PDF metadata and content extraction results in the default dataset

# 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/sample.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("perryay/pdf-metadata-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://example.com/sample.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("perryay/pdf-metadata-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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/sample.pdf"
  ]
}' |
apify call perryay/pdf-metadata-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=perryay/pdf-metadata-extractor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "PDF Metadata & Content Extractor — Extract text & metadata",
        "description": "Download and extract metadata, text content, and page count from PDF documents. Supports batch processing with concurrent downloads.",
        "version": "1.0",
        "x-build-id": "w7edvmcW0mtJzwDNm"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/perryay~pdf-metadata-extractor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-perryay-pdf-metadata-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/perryay~pdf-metadata-extractor/runs": {
            "post": {
                "operationId": "runs-sync-perryay-pdf-metadata-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/perryay~pdf-metadata-extractor/run-sync": {
            "post": {
                "operationId": "run-sync-perryay-pdf-metadata-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "urls"
                ],
                "properties": {
                    "urls": {
                        "title": "PDF URLs",
                        "type": "array",
                        "description": "Array of PDF URLs to extract metadata and content from"
                    },
                    "extractText": {
                        "title": "Extract Text Content",
                        "type": "boolean",
                        "description": "Extract full text content from each PDF",
                        "default": true
                    },
                    "extractMetadata": {
                        "title": "Extract Metadata",
                        "type": "boolean",
                        "description": "Extract document metadata (title, author, subject, etc.)",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
