Render Markdown to PDF
Renders Markdown source into a PDF. The caller may select a font cascade via the fontStack field (an ordered list of hosted family keys — every family is available on every plan) or bring Google Fonts families by passing googleFontsUrl (legacy alias of the first entry) or googleFontsUrls (one or two CSS API, specimen, or share URLs copied from fonts.google.com; available on every plan). googleFontsUrl and googleFontsUrls are mutually exclusive. URLs are merged into a pool (max 4 distinct families). Positional defaults: first family of URL 1 → body; first family of URL 2 (or second family of URL 1 when only one URL) → heading. The optional typography object unifies pool overrides, up to two custom named roles (each binding a pool family or hosted key to a name referenced by rule set.font), per-family static weight maps (variantMap), and hosted heading variant policy (headingVariant). Body, heading, and per-depth heading families are assigned with rules (select + set.family); positional URL defaults apply otherwise. set.family accepts hosted weighted keys, Google display names from the pool, or named-role references. With Google Fonts present, the Google family takes the body/heading slot, so a weighted hosted family left in fontStack is ignored and reported as a fontstack_weighted_dropped note in X-Render-Typography-Notes; the stack then supplies only emoji and code families (omitting it applies the companion default). A malformed or unsupported Google Fonts URL returns 400 invalid_google_fonts_url; a family that does not exist on Google Fonts returns 400 google_font_not_found. When a non-default body/heading font cannot cover characters used in the document (for example list markers or accented letters), the render automatically falls back to the default inter family for body and heading text; the response then carries X-Render-Font-Fallback: inter plus the glyph diagnostics that triggered the fallback. glyphFallback lets the caller choose a different family for this fallback instead of the automatic inter default — a document-level, last-resort family applied per glyph, so only the codepoints the primary fonts miss route to it while every codepoint the primary fonts already cover keeps its requested family; when set, X-Render-Font-Fallback reports the chosen family instead of inter. Images are accepted via standard Markdown syntax (data: URLs are inlined; http(s): URLs are fetched). Remote images are subject to strict security policies (private IP addresses are rejected and mixed content is disabled). For accessibility, set the document language with viewer.language (a BCP 47 tag such as es-ES), which becomes the PDF /Lang; viewer also carries page-layout, page-mode, and viewer-preference hints. Rendering issues are reported via X-Render-Status, X-Render-Rendered-As-Requested, and when present X-Render-Diagnostics (JSON array of actionable objects).
Identical markdown and configuration are deduplicated per account for 60 seconds via a canonical content digest and an in-process LRU cache: cache hits return the stored PDF without re-rendering or billing (x-blinkpdf-cache: hit). Authenticated renders echo that digest as the x-blinkpdf-content-digest response header — the same value POST /v1/render/validate returns — so a client can correlate a validate with its later render. It is a digest of the request/config, not a hash of the output PDF bytes: an inert change (a rule matching nothing) shifts the digest without changing the rendered bytes, so compare the file bytes to detect a visual change. Optional x-idempotency-key still deduplicates safe client retries across the billing period; reusing a key with a different body returns 409 idempotency_conflict before rendering. Caching and idempotency are independent: a cache hit replays bytes without re-rendering, whereas a reused idempotency key can re-render (cache miss) yet still not increment the usage counter. The x-blinkpdf-billing response header reports which of the three outcomes occurred (counted, deduped-cache, deduped-idempotent) — read it rather than inferring billing from the usage counter staying flat.
Pass an optional theme slug to apply a ready-made design preset that resolves server-side to a fixed bundle of typography, page, furniture, numbering, and styling rules, then merged under anything you send yourself — your page/typography/furniture fields and your rules win on every conflict, and declaring your own font (googleFontsUrl(s), typography.body/heading, or a weighted fontStack) drops the theme’s font entirely. Theme fonts are bundled (hosted) families, so a theme never depends on Google at render time and the same source plus the same theme always renders the same bytes. The theme is chosen by you; the service never inspects your Markdown to pick one. An unrecognized slug falls back to report-clean (the unstyled default) without failing the render; POST /v1/render/validate echoes the resolved theme in its resolvedTheme field. Available themes (slug — use it when):
report-clean — generic report or deep-dive; the clean default
report-executive — 1–2 page executive brief or summary
report-data — report dense with tables and KPIs
blog-modern — casual web post or article
blog-dark — screen-first dev post on a dark sheet
blog-editorial — long-form magazine-style narrative
academic-classic — traditional numbered journal paper
academic-modern — modern preprint or working paper
thesis — thesis or long monograph with chapters
docs-light — README, guide, or technical docs
docs-dark — terminal-tone docs or notes
api-reference — API/endpoint reference
corporate — formal company report or memo
proposal — proposal, quote, or SOW
invoice — invoice or receipt
This endpoint always renders bytes; any query parameter (including ?preview=) is rejected with 400 invalid_input (preview modes resolved/layout live on POST /v1/render/validate). Rules that pass validation but never apply (a set property or selector axis not valid for the selected node type, a heading depth outside 1–6, or a rule that matched zero nodes in the rendered document) are reported non-blockingly via the X-Render-Rule-Warnings response header; resolved set values that render inert (a non-square circle mask, an all-zero table widths) are reported via X-Render-Static-Warnings.
Authorizations
API key issued from your Blink PDF account, prefixed bp_. Send it in the x-api-key header on every request. The documentation and health endpoints are exempt.
Headers
Optional client-supplied idempotency token. When present, usage metering deduplicates on (key, user_id) so retries with the same body do not double-count. Reusing the key with a different request body returns 409 idempotency_conflict (no PDF, no usage headers).
Body
Markdown source plus optional document metadata, font selection, page layout, viewer hints, style overrides, and page furniture. Only markdown is required; everything else falls back to safe defaults. When metadata.title is omitted the title is derived from the first Markdown heading; a document with no heading and no title is rendered without a title (no /Title) and is consequently not PDF/UA-1 conformant. Image references use standard Markdown URLs: data: is inline, http(s):// URLs are fetched by the service under strict security defaults (private-IP rejection always-on), and unsupported schemes are reported as render diagnostics. Local images can also be staged out of band and referenced from Markdown as blink://asset/<sha256>, where the handle is the base64url SHA-256 of the bytes; the bytes are fetched at render time and do not count toward the request input size limit. Staged images can also be laid out as pages — one per page or a grid — via the imageSequence option, without hand-authored Markdown. See components.examples.RenderRequest.Minimal, components.examples.RenderRequest.WithRules, components.examples.RenderRequest.WithImages, components.examples.RenderRequest.WithLocalAssets, components.examples.RenderRequest.WithImageSequence, components.examples.RenderRequest.WithOutline, components.examples.RenderRequest.WithHeadingNumbering, components.examples.RenderRequest.WithPageBackground, and components.examples.RenderRequest.WithFurniture for canonical request shapes.
Markdown source plus optional document metadata, font selection, page layout, viewer hints, style overrides, and page furniture. Only markdown is required; everything else falls back to safe defaults. When metadata.title is omitted the title is derived from the first Markdown heading; a document with no heading and no title is rendered without a title (no /Title) and is consequently not PDF/UA-1 conformant. Image references use standard Markdown URLs: data: is inline, http(s):// URLs are fetched by the service under strict security defaults (private-IP rejection always-on), and unsupported schemes are reported as render diagnostics. Local images can also be staged out of band and referenced from Markdown as blink://asset/<sha256>, where the handle is the base64url SHA-256 of the bytes; the bytes are fetched at render time and do not count toward the request input size limit. Staged images can also be laid out as pages — one per page or a grid — via the imageSequence option, without hand-authored Markdown. See components.examples.RenderRequest.Minimal, components.examples.RenderRequest.WithRules, components.examples.RenderRequest.WithImages, components.examples.RenderRequest.WithLocalAssets, components.examples.RenderRequest.WithImageSequence, components.examples.RenderRequest.WithOutline, components.examples.RenderRequest.WithHeadingNumbering, components.examples.RenderRequest.WithPageBackground, and components.examples.RenderRequest.WithFurniture for canonical request shapes.
Markdown source to render to PDF
262144Convenience alias for metadata.title. When both are present metadata.title wins. Defaults to the first Markdown heading when neither is set
1 - 1024\SPDF document metadata embedded in the rendered file. These fields are visible to anyone who opens the PDF — never include PHI, secrets, or other sensitive data.
Ordered list of hosted font families; earlier families take precedence, later ones provide fallback coverage. Every family is available on every plan. When googleFontsUrl or googleFontsUrls is set, the Google family takes the body/heading slot and any weighted family in the stack is ignored (the stack then supplies only the emoji and code roles). Emoji families are appended automatically when omitted
1 - 4 elementsinter, lora, source-serif-4, noto-emoji-mono, twemoji-mozilla, jetbrains-mono Unified font selection: pool declaration/overrides, custom named roles, hosted heading variant policy, and per-family static weight maps for the four-variant PDF model. Body, heading, and per-depth heading families are assigned with rules (select + set.family); positional URL defaults apply otherwise
A text family: a hosted weighted key, a declared Google Fonts display name, or a typography.named role reference
"inter"A single Google Fonts URL (alias of googleFontsUrls[0]). Do not pass together with googleFontsUrls. Accepted forms: CSS API (https://fonts.googleapis.com/css2?family=…), specimen (https://fonts.google.com/specimen/<Family>), or share (https://fonts.google.com/share?selection.family=…). Weight axes set the min/max endpoint weights fetched for the four-variant model. Up to 4 families per URL, and at most 4 in the merged pool across both URLs (two URLs do not raise the total)
2048^https://fonts\.(googleapis|google)\.com/\S+$One or two Google Fonts URLs merged into a single family pool (max 4 distinct families). Default: first family of the first URL → body; first family of the second URL (or second family of the first URL) → heading. Use a rule set.family (or reorder URLs) to override
1 - 2 elementsA single Google Fonts URL (alias of googleFontsUrls[0]). Do not pass together with googleFontsUrls. Accepted forms: CSS API (https://fonts.googleapis.com/css2?family=…), specimen (https://fonts.google.com/specimen/<Family>), or share (https://fonts.google.com/share?selection.family=…). Weight axes set the min/max endpoint weights fetched for the four-variant model. Up to 4 families per URL, and at most 4 in the merged pool across both URLs (two URLs do not raise the total)
2048^https://fonts\.(googleapis|google)\.com/\S+$Page geometry and appearance: size (named preset or [width, height] in points), orientation, margins, and optional full-sheet background
PDF viewer hints — document language, page layout, page mode, and viewer preferences (toolbar/menubar visibility, fit-to-window, duplex, print scaling)
Surgical presentation rules over standard Markdown nodes.
256Ergonomic per-image style overrides by position or exact URL, without authoring a full rules selector entry. For document-order indexing (true reading order across interleaved block and inline images), or for matching by anything other than an exact URL, use rules with select.nth/select.url and type: 'image' instead.
256Design preset resolved server-side to a fixed bundle of typography/page/furniture/numbering/rules, then merged under your own typography/page/furniture/rules (your values win per field; your rules win on conflicting cascade leaves). Picking a theme never lets the server read your Markdown — the bundle is deterministic, so the same source plus the same theme yields the same PDF. An unknown or malformed slug resolves to report-clean and never fails the request; the fallback is reported by resolvedTheme on /v1/render/validate and the x-render-theme-fallback response header on /v1/render. One of: report-clean, report-executive, report-data, blog-modern, blog-dark, blog-editorial, academic-classic, academic-modern, thesis, docs-light, docs-dark, api-reference, corporate, proposal, invoice.
64"report-clean"
"report-executive"
"report-data"
"blog-modern"
"blog-dark"
"blog-editorial"
"academic-classic"
"academic-modern"
"thesis"
"docs-light"
"docs-dark"
"api-reference"
"corporate"
"proposal"
"invoice"
Auto-numbering policy. Opt-in via enabled: true. Counters reset on the next deeper-depth heading and propagate across the document. Passing any of all, byDepth, depthRange, or trailingSeparator together with enabled: false returns 400 — those fields configure an active numbering policy, so they require enabled: true.
Page furniture (running headers and footers). slots applies to every page; set omitFirstPage: true to suppress furniture on the cover page, or pass firstPage to override the first page only. Set omitLastPage: true to suppress furniture on the closing page, or pass lastPage to override the final page only (when totalPages > 1). Optional pageOverrides supplies arbitrary per-page exceptions (later matches win per field). Optional watermark draws faint text behind body content. Optional decorations draws band rules such as masthead hairlines
PDF outline (bookmarks). When omitted, an outline is emitted with the default depth range [1, 6] for any document containing headings. Pass depthRange alone to constrain depths. Set enabled: false to suppress the outline entirely. Passing depthRange together with enabled: false returns 400 — choose one intent.
Lay out staged images as pages without hand-authored Markdown: one per page or tiled in a grid, fit contain/cover, orientation: auto matching each page to its image. Defaults: fit is contain for one_per_page and cover for grid; orientation is auto; size is A4; padding is 0 (full-bleed). The sequence renders after the Markdown body; with empty markdown the document is the sequence alone. Each image still decodes against the per-render megapixel cap, so downscale very large folders or split them across renders. See components.examples.RenderRequest.WithImageSequence
When true, each remote http(s):// image is fetched once and its bytes are reused on later renders of the same request, so repeated renders produce a byte-identical PDF even if the origin changes; reuse lasts up to 24 hours, after which the image is fetched again. Defaults to false, fetching every remote image fresh on each render.
Response
PDF bytes. Every successful render includes X-Render-Status (ok or degraded) and X-Render-Rendered-As-Requested (true when the PDF matches the requested content: no error-severity issues such as missing images or glyphs, and no image-fetch-skipped warnings). When issues occurred, X-Render-Diagnostics carries a base64url-encoded JSON array of objects with severity, code, message, optional resource and httpStatus, and help (a blinkpdf://reference/… URI). If the array was truncated to fit header size limits, X-Render-Diagnostics-Truncated is true (the header may be omitted when even a single entry cannot fit). When the request maps a font weight that the Google Fonts URL does not fetch, X-Render-Typography-Notes carries the same weight_not_in_fetch advisory (base64url JSON) that POST /v1/render/validate reports, so a direct render is not silent about it. Public free-plan API renders carry a non-removable bottom-center footer ("Made with Blink PDF" plus the Blink PDF brand mark); the response schema and status code are unchanged.
The response is of type file.