# Doc To Markdown (`abotapi/doc-to-markdown`) Actor

Convert documents (PDF, Word, PowerPoint, Excel, HTML, images) to clean Markdown. Supports batch processing, metadata extraction, and customizable output formatting.

- **URL**: https://apify.com/abotapi/doc-to-markdown.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Developer tools, AI, SEO tools
- **Features:** Limited permissions
- **Stats:** 19 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 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.
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

## Doc to Markdown: Convert PDF, Word, Excel and More into Clean Text

Doc to Markdown turns documents into clean Markdown text. Paste public URLs, upload files directly, or mix both in the same run, and it converts PDF, Word (.docx), PowerPoint (.pptx), Excel (.xlsx, .xls), HTML, plain text, Markdown, JSON, CSV, ZIP archives, EPUB, Outlook .msg files and Jupyter notebooks, plus a speech to text transcript from short WAV, MP3, M4A and MP4 audio. Read the results as a structured Dataset, download ready to open `.md` files from the Key-Value Store, or both from the same run.

### Why This Scraper?

- **Two ways in, one run.** Paste public URLs, upload files directly, or mix both in the same call.
- **Wide format coverage.** PDF, Word, PowerPoint, Excel (.xlsx and .xls), HTML, plain text, Markdown, JSON, CSV, ZIP archives, EPUB, Outlook .msg files and Jupyter notebooks all convert to Markdown; short WAV, MP3, M4A and MP4 audio gets a speech to text transcript.
- **Every file gets a result.** A failed download or an unsupported file returns its own `success: false` record with an error message instead of stopping the whole run.
- **Two destinations, your choice.** Save results to a structured Dataset, download ready to open `.md` files from the Key-Value Store, or both from the same input.
- **Markdown formatted your way.** Turn file metadata on or off, auto generate a table of contents, and pick ATX or Setext heading style.
- **Batch friendly.** Send any number of URLs and uploaded files in a single call and get one record back per file.

### Use Cases

- **Content and knowledge teams:** turn PDFs, Word docs and slide decks into Markdown for a wiki, CMS or static site.
- **RAG and AI pipelines:** convert manuals, reports and spreadsheets into clean text an LLM can ingest.
- **Data migration:** bulk convert an old document archive, including zipped folders, into a portable Markdown format.
- **Meeting and call archives:** get a text transcript of short WAV or MP3 recordings alongside the source file.
- **Developers and support teams:** convert Jupyter notebooks, Outlook .msg exports or CSV exports into readable Markdown for documentation.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `filename` | `"quarterly-report.pdf"` |
| `success` | `true` |
| `error` | `null` (an error message string when `success` is `false`) |
| `format` | `"pdf"` (the file extension that was detected) |
| `size_bytes` | `245678` |
| `conversion_time_ms` | `812` |
| `markdown` | the converted document text; starts with a metadata block when Include Metadata is on, and a table of contents when Generate Table of Contents is on |
| `metadata` | object with the fields below |
| `metadata.converted_at` | `"2024-01-15T10:30:00"` |
| `metadata.file_size_bytes` | `245678` |
| `metadata.content_type` | `"application/pdf"` (files fetched from a link, when the server sends a Content-Type header) |
| `metadata.last_modified` | `"Mon, 15 Jan 2024 10:00:00 GMT"` (files fetched from a link, when the server sends one) |
| `metadata.format` | `"PDF"`, present only for PDF, DOCX, PPTX, XLSX, HTML/HTM, JPEG and PNG; absent for CSV, ZIP, EPUB, .msg, notebooks, plain text and audio |
| `metadata.format_description` | `"Portable Document Format"`, paired with `metadata.format` |
| `<filename>.md` | Key-Value Store key, saved when Output Format is Key-Value Store or Both, for example `quarterly-report.md` |
| `text/markdown` | the content type the `.md` file above is saved with |

### How to Use

1. Add the documents to convert: paste links under **URLs**, upload files under **Upload Files**, or use both in the same run.
2. Choose your Markdown formatting: **Include Metadata**, **Generate Table of Contents**, and **Heading Style** (ATX or Setext).
3. Pick where results go with **Output Format**: Dataset, Key-Value Store, or both.
4. Click **Start**, then download the Dataset as JSON or CSV, or open the Markdown files from the Key-Value Store.

**Convert a batch of URLs:**

```json
{
  "urls": ["https://example.com/files/quarterly-report.pdf", "https://example.com/files/handbook.docx"]
}
```

**Add a table of contents and Setext headings:**

```json
{
  "urls": ["https://example.com/files/manual.pdf"],
  "includeToc": true,
  "headingStyle": "setext"
}
```

**Dataset only, no metadata block:**

```json
{
  "urls": ["https://example.com/files/notes.txt"],
  "includeMetadata": false,
  "outputFormat": "dataset"
}
```

**Convert a file already stored in one of your Key-Value Stores to a downloadable Markdown file:**

```json
{
  "files": ["<KEY_VALUE_STORE_ID>#contract.pdf"],
  "outputFormat": "keyValueStore"
}
```

Each `files` entry can be a file uploaded in the Console, a direct file link, or a `<storeId>#<key>` reference to a record in one of your Key-Value Stores. The key's extension (for example `.pdf`) tells the actor how to read the file.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/doc-to-markdown").call(run_input={"urls": ["https://example.com/files/report.pdf"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["filename"], item["success"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/doc-to-markdown').call({ urls: ['https://example.com/files/report.pdf'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Choosing Memory (MB) for large or many files

Memory scales automatically with how many files and URLs you send in one run (roughly 256 MB per item, 512 MB minimum). Set **Memory (MB)** yourself, up to 8192, if you are converting a single very large file rather than many small ones.

#### Every file gets its own outcome

A file that cannot be downloaded, is corrupt, or has an unsupported format does not stop the run. It comes back as its own Dataset record with `success: false` and an `error` message, while every other file in the same run still converts normally. Failed files are not written to the Key-Value Store, so keep Output Format on Dataset or Both if you want to see which files failed.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `files` | array | (none) | Upload files to convert directly (PDF, Word, PowerPoint, Excel, HTML, images and more). |
| `urls` | array | sample | URLs of documents to convert. |
| `includeMetadata` | boolean | `true` | Include file metadata (filename, format, size, conversion time) in the output. |
| `includeToc` | boolean | `false` | Auto generate a table of contents from the document's headings. |
| `headingStyle` | string | `atx` | Markdown heading style: `atx` (`# Heading`) or `setext` (underlined; applies to level 1 and 2 headings, deeper levels stay `#` style). |
| `outputFormat` | string | `both` | Where to save results: `dataset`, `keyValueStore`, or `both`. |
| `memoryMbytes` | integer | auto | Override automatic memory allocation, from 256 to 8192 MB. Leave empty to scale automatically with the number of files and URLs. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "filename": "quarterly-report.pdf",
  "success": true,
  "error": null,
  "format": "pdf",
  "size_bytes": 245678,
  "conversion_time_ms": 812,
  "markdown": "---\nfilename: quarterly-report.pdf\nformat: pdf\nsize: 245678 bytes\nconversion_time: 812ms\nconverted_at: 2024-01-15T10:30:00\ncontent_type: application/pdf\nformat: PDF\nformat_description: Portable Document Format\n---\n\n# Quarterly Report\n\nRevenue grew 12 percent year over year.\n",
  "metadata": {
    "converted_at": "2024-01-15T10:30:00",
    "file_size_bytes": 245678,
    "content_type": "application/pdf",
    "format": "PDF",
    "format_description": "Portable Document Format"
  }
}
```

### Plan Requirement

Check the Pricing tab on the Store page for the current rate. The actor has no proxy or connection settings: URLs are downloaded directly (60 second limit per download), so a source that requires a login or blocks direct requests will fail for that one item while the rest of the batch keeps converting. Memory scales automatically with how many files and URLs you send in one run; raise Memory (MB) yourself only for a single very large file.

### FAQ

#### How much does it cost?

The **Pricing** tab shows the current rate. Converting more files or larger files in one run takes longer and needs more memory, so send only the documents you need.

#### Is it legal to convert documents I do not own?

This actor only converts documents you give it, either by URL or by upload. You are responsible for having the right to access and convert that content; follow the source's terms and the copyright laws that apply to you.

#### Why did one file come back with success: false instead of the whole run failing?

Each file is converted on its own. A blocked or missing URL, a corrupt file, or a format this actor cannot read produces one record with `success: false` and an `error` message, while the rest of the batch still converts. Run the failed item again on its own if you think it should have worked.

#### Can I get an actual Word or PDF file back, not just Markdown text?

No. The output is always Markdown text, either as the `markdown` field in the Dataset or as a `.md` file in the Key-Value Store. It does not produce a converted PDF, DOCX or other binary file.

#### What happens if I convert a plain photo (JPEG or PNG)?

JPEG and PNG files are accepted, but this actor does not read text out of images or generate an AI description of them. A plain photo typically comes back with `success: true` and an empty or near empty `markdown` body, since there is no embedded text for it to extract. Documents that already contain a text layer, such as PDF, Word or HTML, are not affected.

#### Can I use this actor from an AI agent or automation tool?

Yes. It is a normal Apify Actor: call it from the API, an Apify integration, or any MCP client that talks to Apify's actor tooling, and read the results back from the Dataset or Key-Value Store.

### 🔗 Want more AI data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏨 <a href="https://apify.com/abotapi/doc-to-markdown-mcp"><b>Doc To Markdown MCP Server</b></a><br>An MCP server that converts documents to clean Markdown. Convert PDFs, Word docs, Excel...</td><td>🤖 <a href="https://apify.com/abotapi/ai-fetch-python"><b>AI Agent Web Fetcher</b></a><br>An advanced web fetcher that can fetch almost all websites and convert them to...</td></tr>
<tr><td>📱 <a href="https://apify.com/abotapi/universal-media-extractor"><b>Universal Media Extractor</b></a><br>Extract videos, audio, and metadata from 1000+ websites including YouTube, TikTok...</td><td>📱 <a href="https://apify.com/abotapi/tiktok-live-recorder"><b>Tiktok Live Recorder</b></a><br>Record TikTok live streams to MP4 with full metadata, all stream quality URLs, and...</td></tr>
<tr><td>🔍 <a href="https://apify.com/abotapi/ip-location-check"><b>Ip Location Check</b></a><br>Look up geographic locations for IP addresses. Supports batch lookups with country, city...</td><td>🎬 <a href="https://apify.com/abotapi/youtube-transcript-scraper"><b>YouTube Transcript &amp; Subtitle Scraper</b></a><br>Extract transcripts and subtitles from YouTube videos in bulk using video, playlist...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/doc-to-markdown/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `files` (type: `array`):

Upload files to convert (PDF, Word, PowerPoint, Excel, HTML, images, etc.)

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

URLs of documents to convert

## `includeMetadata` (type: `boolean`):

Include file metadata (filename, format, size, conversion time) in output

## `includeToc` (type: `boolean`):

Auto-generate table of contents from headings

## `headingStyle` (type: `string`):

Markdown heading style

## `outputFormat` (type: `string`):

How to output the converted markdown

## `memoryMbytes` (type: `integer`):

Override automatic memory allocation. Leave empty to auto-scale based on number of files/URLs. Use higher values for large PDFs or image-heavy documents with OCR.

## Actor input object example

```json
{
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ],
  "includeMetadata": true,
  "includeToc": false,
  "headingStyle": "atx",
  "outputFormat": "both"
}
```

# 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.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/doc-to-markdown").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.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("abotapi/doc-to-markdown").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.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ]
}' |
apify call abotapi/doc-to-markdown --silent --output-dataset

```

## MCP server setup

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

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/2hVQSFNEf9rjgVb46/builds/4mTTvfVJxo7p0B6zD/openapi.json
