How to Convert Markdown to PDF with cURL

Convert styled Markdown with tables, images, and tagged structure into PDF using cURL and the pdfRest API.
Share this page

This tutorial shows how to convert Markdown to PDF with cURL and the pdfRest Convert to PDF API Tool. The request applies page and table styling, creates tagged output, and maps an uploaded image into the Markdown document in one multipart API call.

Why Convert Markdown to PDF with cURL?

Markdown is efficient for writing release notes, technical instructions, and project documentation, but a .md file is not always appropriate for customers, approvers, or document archives. PDF gives that same content a stable page layout while retaining headings, lists, links, code blocks, tables, and images.

A CI release job offers a practical example. After a build creates Markdown release notes containing a compatibility table and product image, a shell step can submit those assets to pdfRest and save the generated PDF with the release artifacts. Product managers and customers receive a polished document, while engineers continue maintaining the source in version control.

The image mapping and alternate-text settings make the workflow suitable for more than text-only conversion. Combined with configurable typography, table colors, margins, language metadata, and tags, cURL can turn an existing Markdown publishing step into repeatable PDF generation without adding a browser-based renderer to the build environment.

cURL Code Example for Converting Markdown to PDF

#!/bin/sh

# By default, we use the US-based API service. This is the primary endpoint for global use.
API_URL="https://api.pdfrest.com"

# For GDPR compliance and enhanced performance for European users, you can switch to the EU-based service by uncommenting the URL below.
# For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
# API_URL="https://eu-api.pdfrest.com"

# This sample converts Markdown input to a tagged PDF through multipart /pdf.
# It maps a Markdown image target to an uploaded image resource.
INPUT_PATH="/path/to/sample.md"
IMAGE_PATH="/path/to/logo.png"
OPTIONS='{"title":"Structured Content Sample","language":"en-US","enable_tagging":true,"page_setup":{"size":"Letter","orientation":"portrait","margin":{"top":36,"right":42,"bottom":36,"left":42}},"style":{"font":"Arial","heading_font":"Arial","code_font":"Courier","text_size":11,"text_color_rgb":[34,34,34],"heading_scale":1.35,"table":{"column_width_weights":[2,3,2],"keep_header_with_first_row":true,"repeat_headers_on_overflow":true,"show_borders":true,"border_width":0.75,"border_color_rgb":[180,188,200],"header_fill_color_rgb":[33,64,98],"header_text_color_rgb":[255,255,255],"row_fill_color_rgb":[250,250,252],"alternate_row_fill_color_rgb":[235,240,246],"cell_padding":{"top":6,"right":8,"bottom":6,"left":8}}},"markdown":{"image_alt_text":{"sample-logo":"Sample logo"},"missing_image_alt_text":"fail","image_sources":{"sample-logo":{"upload_index":0}}}}'

curl --location "$API_URL/pdf" \
  --header "Accept: application/json" \
  --header "Content-Type: multipart/form-data" \
  --header "Api-Key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  --form "file=@$INPUT_PATH" \
  --form "image_files=@$IMAGE_PATH" \
  --form "structured_text_options=$OPTIONS" \
  --form "output=pdf_from_markdown"

Source: View the sample on GitHub

Breaking Down the Code

The Markdown shell example calls cURL directly, so no language package is required. API_URL keeps regional routing visible: the active value uses the US service, and the commented alternative selects the EU service.

#!/bin/sh

# By default, we use the US-based API service. This is the primary endpoint for global use.
API_URL="https://api.pdfrest.com"

# For GDPR compliance and enhanced performance for European users, you can switch to the EU-based service by uncommenting the URL below.
# For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
# API_URL="https://eu-api.pdfrest.com"

# This sample converts Markdown input to a tagged PDF through multipart /pdf.
# It maps a Markdown image target to an uploaded image resource.

For this Markdown request, the endpoint excerpt makes the regional service choice explicit:

# By default, we use the US-based API service. This is the primary endpoint for global use.
API_URL="https://api.pdfrest.com"

# For GDPR compliance and enhanced performance for European users, you can switch to the EU-based service by uncommenting the URL below.
# For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
# API_URL="https://eu-api.pdfrest.com"

INPUT_PATH identifies the local .md source. The @ prefix in the later --form argument instructs cURL to upload that file’s contents instead of sending its path as text.

# It maps a Markdown image target to an uploaded image resource.
INPUT_PATH="/path/to/sample.md"
IMAGE_PATH="/path/to/logo.png"

The JSON assigned to OPTIONS is pdfRest-specific rather than shell syntax. title becomes document metadata, language identifies the document language, and enable_tagging explicitly requests logical structure. page_setup selects Letter portrait pages with 36-point margins, while style supplies fonts, text size, color, and format-specific presentation.

{
  "markdown": {
    "image_alt_text": {
      "sample-logo": "Company logo"
    },
    "missing_image_alt_text": "fail",
    "image_sources": {
      "sample-logo": {
        "upload_index": 0
      }
    }
  }
}

The key sample-logo must match the Markdown image target, such as ![Company logo](sample-logo). upload_index: 0 selects the first file supplied through image_files; indexes are zero-based. The alternate-text map supplies meaningful text for that target, and missing_image_alt_text: "fail" stops conversion when a meaningful mapped image lacks alternate text.

The table subsection styles Markdown tables with relative column widths, repeated headers, borders, header and row colors, alternating fills, and cell padding. These values affect Markdown tables found in the source; they do not create a table when the Markdown contains none.

For Markdown conversion, the --form arguments create the multipart fields: file carries the structured source, structured_text_options carries serialized settings, and output names the requested PDF.

  --header "Api-Key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  --form "file=@$INPUT_PATH" \
  --form "image_files=@$IMAGE_PATH" \
  --form "structured_text_options=$OPTIONS" \
  --form "output=pdf_from_markdown"

The Markdown request asks for JSON with Accept, declares multipart form data with Content-Type, and authenticates through Api-Key. Success returns the generated PDF resource and download URL; failure returns API error information.

INPUT_PATH="/path/to/sample.md"
IMAGE_PATH="/path/to/logo.png"
OPTIONS='{"title":"Structured Content Sample","language":"en-US","enable_tagging":true,"page_setup":{"size":"Letter","orientation":"portrait","margin":{"top":36,"right":42,"bottom":36,"left":42}},"style":{"font":"Arial","heading_font":"Arial","code_font":"Courier","text_size":11,"text_color_rgb":[34,34,34],"heading_scale":1.35,"table":{"column_width_weights":[2,3,2],"keep_header_with_first_row":true,"repeat_headers_on_overflow":true,"show_borders":true,"border_width":0.75,"border_color_rgb":[180,188,200],"header_fill_color_rgb":[33,64,98],"header_text_color_rgb":[255,255,255],"row_fill_color_rgb":[250,250,252],"alternate_row_fill_color_rgb":[235,240,246],"cell_padding":{"top":6,"right":8,"bottom":6,"left":8}}},"markdown":{"image_alt_text":{"sample-logo":"Sample logo"},"missing_image_alt_text":"fail","image_sources":{"sample-logo":{"upload_index":0}}}}'

curl --location "$API_URL/pdf" \
  --header "Accept: application/json" \
  --header "Content-Type: multipart/form-data" \
  --header "Api-Key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  --form "file=@$INPUT_PATH" \
  --form "image_files=@$IMAGE_PATH" \
  --form "structured_text_options=$OPTIONS" \
  --form "output=pdf_from_markdown"

Beyond the Tutorial

This example shows how cURL can generate a polished, tagged PDF from Markdown while controlling presentation and resolving a local image in the same multipart request. Enabling tags adds logical document structure, but it does not establish compliance with a particular accessibility standard.

Simpler Markdown workflows can omit image mapping or styling fields they do not use. Explore request variations in API Lab, and see the Convert to PDF documentation for the complete parameter reference.

Generate a self-service API Key now!
Create your FREE API Key to start processing PDFs in seconds, only possible with pdfRest.