Markdown Heading Extractor
List every heading in a Markdown document with its level, text and an anchor-style slug. Paste your Markdown and press Extract; the server reads hash-style headings from level 1 to 6, skips fenced code blocks, and returns a table plus JSON you can copy or download as markdown-headings.json.
Use Markdown Heading Extractor
Read Content
Turn a Markdown document's headings into structured data
Headings are the skeleton of any Markdown document. They decide the table of contents, the anchor links people share, and how readers and search engines understand the page. The Markdown Heading Extractor reads a document and returns every heading as a row with its level, its text and a URL-friendly slug, plus a JSON copy you can use in scripts. It is a quick way to review an outline, build navigation, or check that anchors match what your site generates.
What is extracted
The tool recognises ATX headings, the style that starts a line with one to six hash signs followed by a space:
# Title is level 1, ## Section is level 2, and so on down to ###### Detail at level 6.
For each heading the JSON contains four keys:
| Key | Example | Meaning |
|---|---|---|
level | 2 | Number of hash signs, 1 to 6 |
text | Install on Windows | Heading text with surrounding spaces trimmed |
slug | install-on-windows | Anchor-style identifier generated from the text |
line | ## Install on Windows | The original source line |
The Extracted Headings table on the page shows level, text and slug; the line value appears in the Raw JSON and the downloaded file.
How slugs are generated
The slug follows a GitHub-style recipe: the text is lower-cased, every character that is not a letter, digit, space or hyphen is removed, spaces become hyphens, repeated hyphens collapse into one, and hyphens at either end are trimmed. Letters from any alphabet are kept, so headings in German, Spanish, Hindi or Japanese produce readable slugs.
A few examples show the rules in action:
Hello World!becomeshello-world.What's new in 2.0?becomeswhats-new-in-20.API_v2 `config`becomesapiv2-config, because underscores and backticks are removed.
Two differences from GitHub are worth knowing. GitHub keeps underscores in anchors, while this tool removes them. And when two headings have the same text, GitHub adds -1, -2 to later ones; this tool gives them identical slugs. If you build links for a GitHub-rendered README, check headings with underscores or duplicate names by hand.
A short example
Take this outline from a project guide:
# Acme CLI## Installation### macOS & Linux## Usage
The extractor returns four rows. Acme CLI is level 1 with the slug acme-cli. Installation and Usage are level 2 with the slugs installation and usage. The third line becomes level 3 with the slug macos-linux: the ampersand is removed and the two spaces it leaves behind turn into a single hyphen. In the JSON each row also carries the original line, which is useful when you want to search for and replace a heading in the source file later. Reading just the level column, 1, 2, 3, 2, you can already see the document's shape without opening it.
Using the extractor
- Paste your Markdown into Input Markdown, or press Paste in the header. The footer shows the character count and a live "possible headings" count.
- Press Extract. The button shows "Working" while the request runs.
- Review the Extracted Headings table to see the outline.
- Use the copy icon (Copy JSON) to copy the array, or the download icon (Download JSON) to save
markdown-headings.json. - The trash icon on the input card clears the input, table and JSON.
The possible headings count is a quick estimate made in the browser and also counts hash lines inside code blocks. The table after extraction is the accurate result, because code blocks are skipped on the server.
Processing and privacy
Extraction runs on this site's server. Pressing Extract sends your Markdown there, the headings are collected, and the list comes back to your browser. Each request accepts up to 200,000 characters; longer input returns an error message. Lines inside fenced code blocks marked with ``` or ~~~ are skipped, so shell comments and Python comments that start with a hash are not mistaken for headings.
Reasons to extract headings
Review the outline before publishing
A table of levels makes structural problems obvious: two level-1 headings on one page, a jump from level 2 straight to level 4, or a section that is much deeper than the rest. Fixing those helps screen-reader users and keeps the page outline clean for search engines.
Build navigation and in-page links
The slug column gives you the anchor for each section. Combine text and slug to create a custom sidebar, a "jump to" menu or links from another page directly into a section.
Compare structure across documents
Extract headings from a set of similar pages, such as product docs or lesson notes, and compare them in a spreadsheet to make sure every page follows the same template.
Feed scripts and search indexes
The JSON is ready for a script that builds a search index, generates a sitemap of sections, or splits a large file at its level-2 headings.
Tips
- Always put a space after the hash signs.
#Headingwithout a space is not a heading in CommonMark and is not extracted. - Start headings at the beginning of the line. Headings indented with spaces are not picked up.
- Avoid closing hash signs, as in
## Title ##. They stay in the text field as part of the heading, although the slug drops them. - Give each section a unique heading if you rely on slugs for links.
Checking heading hierarchy in practice
A healthy document usually has exactly one level-1 heading, followed by level-2 sections that each contain level-3 subsections, and so on. Read down the Level column after extraction. Any step down by more than one, such as 2 followed by 4, is a skipped level that can confuse assistive technology. Several level-1 rows usually mean the page title was repeated or a section was promoted by mistake.
What it does not do
- Setext headings, written as a line of text underlined with
===or---, are not detected. - HTML headings such as
<h2>are ignored. - Inline Markdown in the heading text, such as bold or links, is kept as written in the text field.
- It does not build a nested Markdown table of contents. For that, use a dedicated table of contents generator.
Limitations
- ATX headings only, starting at the first column with a space after the hashes.
- Slugs drop underscores and are not de-duplicated.
- Input is capped at 200,000 characters and needs a connection to the server.
- JSON is the only export format.
Related tools
When you want a ready-to-paste nested list of links rather than raw data, the Markdown TOC Generator builds one. To number your sections automatically, try Markdown Add Heading Numbers.
Frequently Asked Questions
FAQ
Comments (0)
Describe the issue you encountered so we can investigate and improve the tool.