# HTML to Markdown (/docs/experiments/html-to-markdown)



Fetch a webpage and convert its HTML into clean Markdown for notes, LLM prompts, and content pipelines — all from Cloudflare's edge.

## Features [#features]

* Converts headings, paragraphs, emphasis, links, images, lists, code, and blockquotes
* Prefers `<article>` / `<main>` content when present (falls back to `<body>`)
* Resolves relative link and image URLs against the page URL
* Strips `script`, `style`, `svg`, and similar non-content tags
* Caps HTML input at 1MB and Markdown output at 200k characters
* Stateless; no bindings required

## API Reference [#api-reference]

### GET /markdown [#get-markdown]

Fetches HTML and returns title plus Markdown.

<TypeTable
  type="{
  url: {
    description: &#x22;Page URL (must be http or https)&#x22;,
    type: &#x22;string&#x22;,
    required: true,
  },
}"
/>

#### Example Request [#example-request]

```bash
curl "https://your-worker.workers.dev/markdown?url=https://example.com"
```

#### Success Response [#success-response]

**`url`** `string`

Final page URL after redirects

**`title`** `string | null`

Document `<title>` when present

**`markdown`** `string`

Converted Markdown body

```json
{
  "url": "https://example.com/",
  "title": "Example Domain",
  "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples…"
}
```

#### Error Response [#error-response]

```json
{
  "error": "Expected HTML, got content-type: application/json",
  "code": "NOT_HTML"
}
```

#### Error Codes [#error-codes]

* `400` - Missing or invalid `url` (`INVALID_URL`)
* `400` - Response was not HTML (`NOT_HTML`)
* `502` - Fetch failed (`FETCH_ERROR`)

## Use Cases [#use-cases]

* Turn blog posts into Markdown for note-taking apps
* Prepare page content for LLM prompts without raw HTML noise
* Build content migration / archival pipelines on Workers
* Complement readability extraction with a portable text format

## Limitations [#limitations]

* Does not execute JavaScript; client-rendered content is not included
* Table conversion is not fully structured (tables flatten as block text)
* Complex nested layouts may produce extra blank lines
* Conversion is heuristic, not a full HTML5 / CommonMark guarantee

## Deployment [#deployment]

<Steps>
  <Step>
    ### Click the deploy button [#click-the-deploy-button]

    [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/shrinathsnayak/cloudflare-experiments/tree/main/apps/experiments/html-to-markdown)
  </Step>

  <Step>
    ### Deploy [#deploy]

    Follow the deployment wizard. No bindings or secrets required.
  </Step>

  <Step>
    ### Test your deployment [#test-your-deployment]

    ```bash
    curl "https://your-worker.workers.dev/markdown?url=https://example.com"
    ```
  </Step>
</Steps>

## Local Development [#local-development]

```bash
cd apps/experiments/html-to-markdown
npm install
npm run dev
```

Test locally:

```bash
curl "http://localhost:8787/markdown?url=https://www.cloudflare.com"
```

## Cloudflare Features Used [#cloudflare-features-used]

* **[Workers](https://developers.cloudflare.com/workers/)** - Edge compute runtime
* **[Fetch API](https://developers.cloudflare.com/workers/runtime-apis/fetch/)** - HTML retrieval from the edge

## Next Steps [#next-steps]

<Cards>
  <Card title="Readability Extractor" href="/docs/experiments/readability-extractor">
    Extract clean article content with Browser Rendering
  </Card>

  <Card title="Website to llms.txt" href="/docs/experiments/website-to-llms-txt">
    Convert pages into llms.txt for LLM consumption
  </Card>

  <Card title="JSON-LD Extractor" href="/docs/experiments/json-ld-extractor">
    Extract Schema.org structured data from HTML
  </Card>
</Cards>
