# URL to Markdown Converter (`comradecoding/url-to-markdown`) Actor

Turn public web pages into clean Markdown with title, links, and heading sections. You pay only for pages that convert.

- **URL**: https://apify.com/comradecoding/url-to-markdown.md
- **Developed by:** [Comrade Coding](https://apify.com/comradecoding) (community)
- **Categories:** Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 converted pages

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

## URL to Markdown Converter

Give it a list of public web pages. For each page you get back the text as clean Markdown, plus the title, description, and language. If you want them, you also get a list of every link on the page and the page split into sections by heading.

It is meant for feeding pages to a language model, building a search index, archiving docs, or pasting a page into a notes app without the menus and cookie banners.

### What it does to each page

1. Checks the site's `robots.txt`. If the page is disallowed, it is skipped.
2. Downloads the page (HTML or plain text, up to 5 MB, 30 second timeout).
3. Removes scripts, styles, and embedded frames. With **Strip navigation and page chrome** on, it also removes nav bars, headers, footers, sidebars, forms, buttons, cookie banners, "skip to content" links, and heading permalink markers.
4. With **Extract main content** on, it keeps only the article body, using the same kind of readability algorithm browsers use for reader mode. If a page has no clear main body (a home page, a short landing page), it falls back to the whole page and says so in the `extraction` field.
5. Converts the result to Markdown: headings, lists, bold and italic, code blocks, block quotes, and tables. Relative links and image URLs are made absolute.

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `startUrls` | array | required, no default | Pages to convert. Plain strings or `{ "url": "..." }` objects. Duplicates are dropped. |
| `maxItems` | integer | `100` | Stop after this many converted pages. Caps what a run costs you. |
| `useReadability` | boolean | `true` | Keep only the main content of each page. |
| `stripBoilerplate` | boolean | `true` | Remove navigation, headers, footers, sidebars, and banners. |
| `imageMode` | string | `markdown` | `markdown` keeps `![alt](url)`. `alt-text` writes `[Image: alt text]` and drops the URL. `none` removes images. |
| `includeLinks` | boolean | `false` | Add a `links` array of `{ text, url }`, one per unique http(s) link in the converted content. |
| `includeSections` | boolean | `false` | Add a `sections` array of `{ level, heading, text }`, split at each Markdown heading. Text before the first heading has level 0. |

Example input:

```json
{
    "startUrls": [
        { "url": "https://example.com/" },
        { "url": "https://en.wikipedia.org/wiki/Markdown" }
    ],
    "maxItems": 10,
    "imageMode": "alt-text",
    "includeLinks": true,
    "includeSections": true
}
```

### Output

One dataset item per converted page. This is a real result from a test run:

```json
{
    "source_url": "https://example.com/",
    "final_url": "https://example.com/",
    "fetched_at": "2026-10-28T05:59:51.090Z",
    "status_code": 200,
    "title": "Example Domain",
    "description": null,
    "language": "en",
    "extraction": "full-page",
    "word_count": 25,
    "markdown": "This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.",
    "links": [],
    "sections": [
        {
            "level": 0,
            "heading": "",
            "text": "This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes."
        }
    ]
}
```

Fields:

- `source_url`: the URL you gave.
- `final_url`: where the page ended up after redirects.
- `fetched_at`: ISO 8601 time of the fetch.
- `status_code`: HTTP status of the final response.
- `title`, `description`, `language`: from the page's title, meta description, and `lang` attribute. `null` when missing.
- `extraction`: `readability` (main content found), `full-page` (whole page converted), or `plain-text` (the URL served plain text or Markdown, returned as is).
- `word_count`: rough word count of the Markdown.
- `markdown`: the converted page.
- `links`: only when `includeLinks` is on.
- `sections`: only when `includeSections` is on.

Pages that fail are not added to the dataset, so you do not pay for them. They are listed in the `FAILED_URLS` record in the run's key-value store, each with a reason such as `Disallowed by robots.txt`, `HTTP 404`, or `Unsupported content type: application/pdf`.

### Limits

- Public pages only. It does not log in, send cookies, or fill forms.
- It does not run JavaScript. Pages that build their content in the browser (some single-page apps) come back mostly empty. Check `word_count`.
- HTML and plain text only. PDFs, images, and other files are skipped.
- Pages over 5 MB or slower than 30 seconds are skipped.
- It does not follow links or crawl a site. You list the pages you want.
- Pages are fetched one at a time with a 300 to 800 ms pause between requests to the same site, so large lists take a while. Expect about one page per second when all URLs are on one site.
- If a site's `robots.txt` cannot be reached (server error or timeout), that site's pages are skipped rather than guessed at.
- Main content extraction is a heuristic. On unusual layouts it can keep a sidebar or drop a short intro. Turn it off to get the whole page with only the chrome removed.

### Pricing

Pay per result: you are charged for each page that ends up in the dataset. Skipped and failed pages are free. Set `maxItems` to cap the cost of a run.

This actor is not affiliated with any of the sites used in the examples.

### Use it from an AI agent

Add this actor as a tool through Apify's MCP server: `https://mcp.apify.com?tools=comradecoding/url-to-markdown`. The agent gets one JSON row per page with url, title, description, language, markdown and word_count.

### Integrations

Anything that can call the Apify API can run this actor: the Apify API and client libraries, and Apify's integrations for Make, n8n, Zapier and LangChain. Results come back as a dataset you can download as JSON, CSV or Excel.

### What it costs

| Run size | Cost |
|---|---|
| 100 pages | about $0.20 |
| 1,000 pages | about $2 |
| 10,000 pages | about $20 |

Platform usage is included in the price.

### FAQ

**Does it work on sites that need JavaScript?**

No. It reads the HTML the server sends. Pages built in the browser can come back mostly empty, so check `word_count`.

**Can it crawl a whole site?**

No, you list the pages. To get every page of a site, run Sitemap URL Extractor first and pass its URLs in.

**Am I charged for pages that fail?**

No. Failed and skipped pages never reach the dataset, so they are not charged. They are listed with a reason in FAILED_URLS.

**How fast is it?**

About one page per second on a single site, because of the polite delay between requests.

### Related actors by ComradeCoding

- [Sitemap URL Extractor](https://apify.com/comradecoding/sitemap-url-extractor)
- [RSS and Atom to JSON](https://apify.com/comradecoding/rss-to-json)
- [PDF to Text and JSON](https://apify.com/comradecoding/pdf-to-text)

# Actor input Schema

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

Required. Public pages to convert. One result per page. Pages behind a login, blocked by robots.txt, or not HTML or plain text are skipped and listed in the FAILED_URLS record.

## `maxItems` (type: `integer`):

Stop after this many pages have been converted. You pay per result, so this caps the cost of a run.

## `useReadability` (type: `boolean`):

Keep only the article or main body of the page using a readability algorithm. Falls back to the full page when no clear main content is found (the result's extraction field says which was used).

## `stripBoilerplate` (type: `boolean`):

Remove nav bars, headers, footers, sidebars, forms, buttons, and cookie banners before converting.

## `imageMode` (type: `string`):

markdown: keep images as ![alt](url). alt-text: replace each image with \[Image: alt text], useful for feeding text to a language model. none: drop images.

## `includeLinks` (type: `boolean`):

Add a links field: every unique http(s) link in the converted content, as { text, url } with absolute URLs.

## `includeSections` (type: `boolean`):

Add a sections field: the Markdown split at each heading, as { level, heading, text }. Handy for chunking.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com/"
    },
    {
      "url": "https://en.wikipedia.org/wiki/Markdown"
    }
  ],
  "maxItems": 10,
  "useReadability": true,
  "stripBoilerplate": true,
  "imageMode": "alt-text",
  "includeLinks": true,
  "includeSections": true
}
```

# Actor output Schema

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

No description

## `failures` (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 = {
    "startUrls": [
        {
            "url": "https://example.com/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("comradecoding/url-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 = { "startUrls": [{ "url": "https://example.com/" }] }

# Run the Actor and wait for it to finish
run = client.actor("comradecoding/url-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 '{
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ]
}' |
apify call comradecoding/url-to-markdown --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,comradecoding/url-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/MqTyEuk5BlEMSWZ15/builds/trK4qJ7pqZZNJR5fO/openapi.json
