diff --git a/app/Entities/Tools/ExportFormatter.php b/app/Entities/Tools/ExportFormatter.php
index bd0e1bfd0..9317b0431 100644
--- a/app/Entities/Tools/ExportFormatter.php
+++ b/app/Entities/Tools/ExportFormatter.php
@@ -248,7 +248,7 @@ class ExportFormatter
         $text = "# " . $chapter->name . "\n\n";
         $text .= $chapter->description . "\n\n";
         foreach ($chapter->pages as $page) {
-            $text .= $this->pageToMarkdown($page);
+            $text .= $this->pageToMarkdown($page) . "\n\n";
         }
         return $text;
     }
diff --git a/app/Entities/Tools/Markdown/HtmlToMarkdown.php b/app/Entities/Tools/Markdown/HtmlToMarkdown.php
index d3a2a3e7f..c56119fe1 100644
--- a/app/Entities/Tools/Markdown/HtmlToMarkdown.php
+++ b/app/Entities/Tools/Markdown/HtmlToMarkdown.php
@@ -53,7 +53,21 @@ class HtmlToMarkdown
      */
     protected function getConverterEnvironment(): Environment
     {
-        $environment = new Environment(['header_style' => 'atx']);
+        $environment = new Environment([
+            'header_style' => 'atx', // Set to 'atx' to output H1 and H2 headers as # Header1 and ## Header2
+            'suppress_errors' => true, // Set to false to show warnings when loading malformed HTML
+            'strip_tags' => false, // Set to true to strip tags that don't have markdown equivalents. N.B. Strips tags, not their content. Useful to clean MS Word HTML output.
+            'strip_placeholder_links' => false, // Set to true to remove <a> that doesn't have href.
+            'bold_style' => '**', // DEPRECATED: Set to '__' if you prefer the underlined style
+            'italic_style' => '*', // DEPRECATED: Set to '_' if you prefer the underlined style
+            'remove_nodes' => '', // space-separated list of dom nodes that should be removed. example: 'meta style script'
+            'hard_break' => false, // Set to true to turn <br> into `\n` instead of `  \n`
+            'list_item_style' => '-', // Set the default character for each <li> in a <ul>. Can be '-', '*', or '+'
+            'preserve_comments' => false, // Set to true to preserve comments, or set to an array of strings to preserve specific comments
+            'use_autolinks' => false, // Set to true to use simple link syntax if possible. Will always use []() if set to false
+            'table_pipe_escape' => '\|', // Replacement string for pipe characters inside markdown table cells
+            'table_caption_side' => 'top', // Set to 'top' or 'bottom' to show <caption> content before or after table, null to suppress
+        ]);
 
         $environment->addConverter(new BlockquoteConverter());
         $environment->addConverter(new CodeConverter());
diff --git a/app/Http/Controllers/Api/BookExportApiController.php b/app/Http/Controllers/Api/BookExportApiController.php
index 3d813c4d4..24cba9df3 100644
--- a/app/Http/Controllers/Api/BookExportApiController.php
+++ b/app/Http/Controllers/Api/BookExportApiController.php
@@ -44,4 +44,14 @@ class BookExportApiController extends ApiController
         $textContent = $this->exportFormatter->bookToPlainText($book);
         return $this->downloadResponse($textContent, $book->slug . '.txt');
     }
+
+    /**
+     * Export a book as a markdown file.
+     */
+    public function exportMarkdown(int $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $markdown = $this->exportFormatter->bookToMarkdown($book);
+        return $this->downloadResponse($markdown, $book->slug . '.md');
+    }
 }
diff --git a/app/Http/Controllers/Api/ChapterExportApiController.php b/app/Http/Controllers/Api/ChapterExportApiController.php
index afdfe555d..a4c349f4e 100644
--- a/app/Http/Controllers/Api/ChapterExportApiController.php
+++ b/app/Http/Controllers/Api/ChapterExportApiController.php
@@ -2,7 +2,6 @@
 
 use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Tools\ExportFormatter;
-use BookStack\Entities\Repos\BookRepo;
 use Throwable;
 
 class ChapterExportApiController extends ApiController
@@ -48,4 +47,14 @@ class ChapterExportApiController extends ApiController
         $textContent = $this->exportFormatter->chapterToPlainText($chapter);
         return $this->downloadResponse($textContent, $chapter->slug . '.txt');
     }
+
+    /**
+     * Export a chapter as a markdown file.
+     */
+    public function exportMarkdown(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $markdown = $this->exportFormatter->chapterToMarkdown($chapter);
+        return $this->downloadResponse($markdown, $chapter->slug . '.md');
+    }
 }
diff --git a/app/Http/Controllers/Api/PageExportApiController.php b/app/Http/Controllers/Api/PageExportApiController.php
index 7563092cb..bf43016c2 100644
--- a/app/Http/Controllers/Api/PageExportApiController.php
+++ b/app/Http/Controllers/Api/PageExportApiController.php
@@ -44,4 +44,14 @@ class PageExportApiController extends ApiController
         $textContent = $this->exportFormatter->pageToPlainText($page);
         return $this->downloadResponse($textContent, $page->slug . '.txt');
     }
+
+    /**
+     * Export a page as a markdown file.
+     */
+    public function exportMarkdown(int $id)
+    {
+        $page = Page::visible()->findOrFail($id);
+        $markdown = $this->exportFormatter->pageToMarkdown($page);
+        return $this->downloadResponse($markdown, $page->slug . '.md');
+    }
 }
diff --git a/routes/api.php b/routes/api.php
index 44643d6d4..5b724fab1 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -18,6 +18,7 @@ Route::delete('books/{id}', 'BookApiController@delete');
 Route::get('books/{id}/export/html', 'BookExportApiController@exportHtml');
 Route::get('books/{id}/export/pdf', 'BookExportApiController@exportPdf');
 Route::get('books/{id}/export/plaintext', 'BookExportApiController@exportPlainText');
+Route::get('books/{id}/export/markdown', 'BookExportApiController@exportMarkdown');
 
 Route::get('chapters', 'ChapterApiController@list');
 Route::post('chapters', 'ChapterApiController@create');
@@ -28,6 +29,7 @@ Route::delete('chapters/{id}', 'ChapterApiController@delete');
 Route::get('chapters/{id}/export/html', 'ChapterExportApiController@exportHtml');
 Route::get('chapters/{id}/export/pdf', 'ChapterExportApiController@exportPdf');
 Route::get('chapters/{id}/export/plaintext', 'ChapterExportApiController@exportPlainText');
+Route::get('chapters/{id}/export/markdown', 'ChapterExportApiController@exportMarkdown');
 
 Route::get('pages', 'PageApiController@list');
 Route::post('pages', 'PageApiController@create');
@@ -38,6 +40,7 @@ Route::delete('pages/{id}', 'PageApiController@delete');
 Route::get('pages/{id}/export/html', 'PageExportApiController@exportHtml');
 Route::get('pages/{id}/export/pdf', 'PageExportApiController@exportPdf');
 Route::get('pages/{id}/export/plaintext', 'PageExportApiController@exportPlainText');
+Route::get('pages/{id}/export/markdown', 'PageExportApiController@exportMarkDown');
 
 Route::get('shelves', 'BookshelfApiController@list');
 Route::post('shelves', 'BookshelfApiController@create');
diff --git a/tests/Api/BooksApiTest.php b/tests/Api/BooksApiTest.php
index a36acdd02..446ba2811 100644
--- a/tests/Api/BooksApiTest.php
+++ b/tests/Api/BooksApiTest.php
@@ -140,4 +140,17 @@ class BooksApiTest extends TestCase
         $resp->assertStatus(200);
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"');
     }
+
+    public function test_export_markdown_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->has('pages')->has('chapters')->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/markdown");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.md"');
+        $resp->assertSee('# ' . $book->name);
+        $resp->assertSee('# ' . $book->pages()->first()->name);
+        $resp->assertSee('# ' . $book->chapters()->first()->name);
+    }
 }
\ No newline at end of file
diff --git a/tests/Api/ChaptersApiTest.php b/tests/Api/ChaptersApiTest.php
index c7368eaee..e11bb0e1c 100644
--- a/tests/Api/ChaptersApiTest.php
+++ b/tests/Api/ChaptersApiTest.php
@@ -186,4 +186,16 @@ class ChaptersApiTest extends TestCase
         $resp->assertStatus(200);
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
     }
+
+    public function test_export_markdown_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->has('pages')->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/markdown");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.md"');
+        $resp->assertSee('# ' . $chapter->name);
+        $resp->assertSee('# ' . $chapter->pages()->first()->name);
+    }
 }
\ No newline at end of file
diff --git a/tests/Api/PagesApiTest.php b/tests/Api/PagesApiTest.php
index e08e9b1b7..9fab675e7 100644
--- a/tests/Api/PagesApiTest.php
+++ b/tests/Api/PagesApiTest.php
@@ -258,4 +258,15 @@ class PagesApiTest extends TestCase
         $resp->assertStatus(200);
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
     }
+
+    public function test_export_markdown_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/markdown");
+        $resp->assertStatus(200);
+        $resp->assertSee('# ' . $page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"');
+    }
 }
\ No newline at end of file