MD2PDF REST API

Create, update, and share Markdown documents programmatically.

The public API lets AI agents, scripts, and integrations produce styled, shareable documents in Markdown — no signup, no API key. Rate-limited to 10 writes (save, update and delete combined) per IP per minute.

Try in Browser AI Skill llms-full.txt

Two paths, different threat models

Path 1 — End-to-end encrypted (recommended). Encrypt locally with AES-256-GCM and send ciphertext with header X-Encrypted: aes-256-gcm. The server stores the blob as-is and never sees the key or plaintext. Use this for anything non-public.

Path 2 — Server-side encryption (legacy). Send plaintext; the server generates the key, encrypts, and returns the key. The server sees plaintext during the request. Only suitable for content that is already public.

Endpoints

POST /api/save — Path 1 (E2EE, recommended)

Python example. Generates a 256-bit key locally, encrypts, POSTs ciphertext.

import os, json, base64, urllib.request
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

markdown = "# Hello\n\nThis is a shared document."

key = AESGCM.generate_key(bit_length=256)
iv  = os.urandom(12)
ct  = AESGCM(key).encrypt(iv, markdown.encode(), None)
body = base64.b64encode(iv + ct).decode()
key_b64url = base64.urlsafe_b64encode(key).rstrip(b"=").decode()

req = urllib.request.Request(
    "https://md2pdf.studio/api/save",
    data=body.encode(),
    headers={"Content-Type": "text/plain", "X-Encrypted": "aes-256-gcm"},
    method="POST",
)
data = json.loads(urllib.request.urlopen(req).read())
print(f"{data['url']}#k={key_b64url}")

Response (no key field — the server does not have it):

{
  "id": "aB3xY9zK",
  "editKey": "…",
  "url": "https://md2pdf.studio/s/aB3xY9zK"
}

POST /api/save — Path 2 (plaintext, public content only)

curl -X POST https://md2pdf.studio/api/save \
  -H "Content-Type: text/plain" \
  --data-binary "# Public release notes"

Response:

{
  "id": "aB3xY9zK",
  "editKey": "…",
  "url": "https://md2pdf.studio/s/aB3xY9zK",
  "key": "…"   // AES-256-GCM key generated server-side after reading your plaintext
}

PUT /api/update/:id

Path 1 (E2EE): reuse the same key, generate a fresh IV, send ciphertext. Do not send X-Enc-Key.

curl -X PUT https://md2pdf.studio/api/update/aB3xY9zK \
  -H "Content-Type: text/plain" \
  -H "X-Edit-Key: YOUR_EDIT_KEY" \
  -H "X-Encrypted: aes-256-gcm" \
  --data-binary "<base64 ciphertext>"

Path 2 (plaintext):

curl -X PUT https://md2pdf.studio/api/update/aB3xY9zK \
  -H "Content-Type: text/plain" \
  -H "X-Edit-Key: YOUR_EDIT_KEY" \
  -H "X-Enc-Key: YOUR_ENC_KEY" \
  --data-binary "# Hello (updated)"

GET /s/:id

Load a shared document. Every link gets the same generic preview card in chat apps: the server does not read document titles or content.

Rate Limits

Writes per IP (save + update + delete)
10 / minute
Max document size
500 KB
Retention
30 days from creation (links before the Oct 2, 2026 update: 90 days after last update)
Auth
None (anonymous)

Encryption

All documents are encrypted at rest with AES-256-GCM. Path 1 is true end-to-end encryption: the key is generated by the caller and never reaches the server. Path 2 is server-side encryption: the server sees plaintext during the request and the key during the response. Pick based on the sensitivity of the content — the web editor uses Path 1 by default.

The full URL, including #k=, is a bearer token. Treat it like a password: never paste it in channels that may log or cache URLs.

For AI Agents

See /ai-skill for the installable Claude Skill, /llms-full.txt for the full LLM documentation, and /skill.md for the skill manifest.

FAQ

Do I need an API key?

No. The API is anonymous and rate-limited by IP. For higher limits, self-host via the open-source repo.

Is there an OpenAPI / Swagger spec?

Not yet. See llms-full.txt for a complete machine-readable description.

Can I delete a document?

Yes: DELETE /api/delete/{id} with the X-Edit-Key header. Otherwise documents expire automatically 30 days after creation.