{
  "openapi": "3.1.0",
  "info": {
    "title": "PrintFriendly API",
    "version": "2.0.0",
    "summary": "Turn a web page, HTML or Markdown into a PDF, Word file, Markdown, JSON, HTML or text.",
    "description": "One endpoint, POST /v2/convert, takes a URL, some HTML or some Markdown and returns a PDF, a Word file, Markdown, JSON, HTML or plain text. The older /v2/pdf/create, /v2/html/create and /v2/text/create endpoints keep working unchanged.\n\nGet a free key at https://www.printfriendly.com/me/api-key (100 documents a month, no card). Send it as `Authorization: Bearer YOUR_API_KEY`.\n\nCalls are synchronous by default and answer within 27 seconds. Add `async: true` to /v2/convert to run a conversion in the background instead: it answers 202 with a job id, and the result is read from GET /v2/jobs/{id} or POSTed to your `webhook_url`. Only a successful conversion counts toward your plan, however it was run.\n\nAI agents: a plain-text version of these docs is at https://www.printfriendly.com/llms-full.txt.",
    "termsOfService": "https://www.printfriendly.com/terms",
    "contact": {
      "name": "PrintFriendly support",
      "url": "https://www.printfriendly.com/support"
    }
  },
  "servers": [
    {
      "url": "https://api.printfriendly.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Human-readable API docs",
    "url": "https://www.printfriendly.com/api/instructions/v2"
  },
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyHeader": []
    },
    {
      "apiKeyQuery": []
    },
    {
      "basicAuth": []
    }
  ],
  "tags": [
    {
      "name": "Convert",
      "description": "The unified endpoint."
    },
    {
      "name": "Jobs",
      "description": "Background conversions started with async: true."
    },
    {
      "name": "Legacy",
      "description": "The original endpoints. Unchanged."
    }
  ],
  "paths": {
    "/v2/convert": {
      "post": {
        "operationId": "convert",
        "summary": "Convert a URL, HTML or Markdown",
        "description": "Send exactly one of url, html or markdown, and choose an output. pdf and docx are stored for 48 hours and returned as a file_url; markdown, html and text come back in content; json returns the article in pieces in document.\n\nOne successful call is one document on your plan, whatever the output. Failed calls are free.\n\nAsync: add `async: true` and the call answers 202 straight away with a job_id. The job counts once, when it succeeds. Read it at GET /v2/jobs/{id}, or set `webhook_url` and it is POSTed to you.",
        "tags": [
          "Convert"
        ],
        "requestBody": {
          "required": true,
          "description": "JSON, a urlencoded form or multipart.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConvertRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/ConvertRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ConvertRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversion worked and counted once toward your plan.",
            "headers": {
              "X-Request-Id": {
                "description": "Quote this to support.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ConvertFileResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ConvertContentResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ConvertJsonResponse"
                    }
                  ]
                },
                "examples": {
                  "pdf": {
                    "summary": "URL to PDF",
                    "value": {
                      "status": "success",
                      "output": "pdf",
                      "file_url": "https://storage.printfriendly.com/data-production/pdf_1760380000000_a1b2c3Letter.pdf",
                      "expires_at": "2026-10-15T12:00:00.000Z",
                      "size_bytes": 184233,
                      "document": {
                        "title": "Eclipse",
                        "url": "https://en.wikipedia.org/wiki/Eclipse",
                        "site": "en.wikipedia.org",
                        "word_count": 6120
                      },
                      "usage": {
                        "used_this_period": 3,
                        "included": 100,
                        "plan": "free"
                      }
                    }
                  },
                  "markdown": {
                    "summary": "URL to Markdown",
                    "value": {
                      "status": "success",
                      "output": "markdown",
                      "content": "# Eclipse\n\nAn eclipse is an astronomical event...",
                      "document": {
                        "title": "Eclipse",
                        "url": "https://en.wikipedia.org/wiki/Eclipse",
                        "site": "en.wikipedia.org",
                        "word_count": 6120
                      },
                      "usage": {
                        "used_this_period": 4,
                        "included": 100,
                        "plan": "free"
                      }
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Only with async: true. The job was queued. Nothing has been converted, counted or recorded yet: that happens when it runs, and only if it succeeds. Read the result at status_url.",
            "headers": {
              "Location": {
                "description": "Same as status_url.",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "X-Request-Id": {
                "description": "Quote this to support.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertJobAccepted"
                },
                "example": {
                  "status": "queued",
                  "job_id": "5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                  "status_url": "https://api.printfriendly.com/v2/jobs/5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                  "expires_at": "2026-10-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "callbacks": {
          "jobFinished": {
            "{$request.body#/webhook_url}": {
              "post": {
                "operationId": "jobFinishedWebhook",
                "summary": "A job finished",
                "description": "Sent once, when an async job with a webhook_url succeeds or fails. The body is the job object, byte for byte what GET /v2/jobs/{id} returns. X-PrintFriendly-Signature is \"sha256=\" followed by the hex HMAC-SHA256 of the raw body, keyed with the API key that started the job. Answer with any 2xx. Anything else, or no answer in 10 seconds, is retried three times, after 2, 10 and 60 seconds; redirects are not followed. Delivery is best effort and never delays the job. Retries can arrive after a slow success, so use job_id to ignore repeats.",
                "parameters": [
                  {
                    "name": "X-PrintFriendly-Signature",
                    "in": "header",
                    "required": true,
                    "description": "sha256= followed by the hex HMAC-SHA256 of the raw request body, keyed with the API key.",
                    "schema": {
                      "type": "string",
                      "pattern": "^sha256=[0-9a-f]{64}$"
                    }
                  },
                  {
                    "name": "X-PrintFriendly-Job-Id",
                    "in": "header",
                    "required": true,
                    "schema": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                ],
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/Job"
                      }
                    }
                  }
                },
                "responses": {
                  "2XX": {
                    "description": "Delivered. The body of your answer is ignored."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "convertGet",
        "summary": "Convert a URL, HTML or Markdown (query string)",
        "description": "Same as POST, with the parameters in the query string. Handy for curl and for agents that can only fetch URLs.",
        "tags": [
          "Convert"
        ],
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": false,
            "description": "The web page to convert. We fetch it and keep the article. Send exactly one of url, html or markdown.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "page_url",
            "in": "query",
            "required": false,
            "description": "Legacy name for url, accepted so /v2/pdf/create callers can switch by changing the path.",
            "schema": {
              "type": "string",
              "format": "uri",
              "deprecated": true
            }
          },
          {
            "name": "html",
            "in": "query",
            "required": false,
            "description": "HTML to convert, treated as the article body. Scripts and anything unsafe are removed. Up to 2 MB of markup; embedded data: images do not count toward that, and 50 MB is the limit in all.",
            "schema": {
              "type": "string",
              "maxLength": 50000000
            }
          },
          {
            "name": "markdown",
            "in": "query",
            "required": false,
            "description": "Markdown to convert (GitHub flavour). Up to 1 MB.",
            "schema": {
              "type": "string",
              "maxLength": 1000000
            }
          },
          {
            "name": "source_url",
            "in": "query",
            "required": false,
            "description": "With html only. The address the HTML came from. It appears under the title and resolves relative links.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "Heading printed above html or markdown. Ignored for url, which uses the page's own title.",
            "schema": {
              "type": "string",
              "maxLength": 500
            }
          },
          {
            "name": "output",
            "in": "query",
            "required": false,
            "description": "pdf and docx return a file_url. markdown, html and text return content. json returns the whole article in document.",
            "schema": {
              "type": "string",
              "enum": [
                "pdf",
                "docx",
                "markdown",
                "json",
                "html",
                "text"
              ],
              "default": "pdf"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Paper size for pdf and docx. Case does not matter.",
            "schema": {
              "type": "string",
              "enum": [
                "letter",
                "a4"
              ],
              "default": "letter"
            }
          },
          {
            "name": "css_url",
            "in": "query",
            "required": false,
            "description": "A stylesheet applied after ours. pdf and html output.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "custom_css_url",
            "in": "query",
            "required": false,
            "description": "Legacy name for css_url.",
            "schema": {
              "type": "string",
              "format": "uri",
              "deprecated": true
            }
          },
          {
            "name": "header_text",
            "in": "query",
            "required": false,
            "description": "A line of text shown above the title. pdf and html output.",
            "schema": {
              "type": "string",
              "maxLength": 500
            }
          },
          {
            "name": "header_image_url",
            "in": "query",
            "required": false,
            "description": "An image shown above the title, such as a logo. pdf and html output.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Language of the content. Defaults to the page's own.",
            "schema": {
              "type": "string",
              "examples": [
                "en",
                "pt-BR"
              ]
            }
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "description": "Text direction. Defaults to the page's own.",
            "schema": {
              "type": "string",
              "enum": [
                "ltr",
                "rtl"
              ]
            }
          },
          {
            "name": "include_links",
            "in": "query",
            "required": false,
            "description": "Keep link and image addresses in markdown and text output. Off by default: links keep their words and lose their addresses.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "async",
            "in": "query",
            "required": false,
            "description": "Run the conversion in the background. The answer is 202 with a job_id and a status_url straight away; nothing is converted or counted until the job runs. Read the result with GET /v2/jobs/{id} or give a webhook_url. The result is exactly the body a synchronous call returns.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "Another way to write async: \"async\" is async: true. If you send both they must agree, otherwise the request is a 400.",
            "schema": {
              "type": "string",
              "enum": [
                "sync",
                "async"
              ],
              "default": "sync"
            }
          },
          {
            "name": "webhook_url",
            "in": "query",
            "required": false,
            "description": "Only with async. An https address on a public host that we POST the finished job to, once, as JSON (the same object GET /v2/jobs/{id} returns). Signed in X-PrintFriendly-Signature. Addresses that point at a private or internal network are refused with a 400. Delivery is retried three times and is best effort: the result is always readable at status_url.",
            "schema": {
              "type": "string",
              "format": "uri",
              "maxLength": 2048,
              "pattern": "^https://"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The conversion worked and counted once toward your plan.",
            "headers": {
              "X-Request-Id": {
                "description": "Quote this to support.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ConvertFileResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ConvertContentResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ConvertJsonResponse"
                    }
                  ]
                },
                "examples": {
                  "pdf": {
                    "summary": "URL to PDF",
                    "value": {
                      "status": "success",
                      "output": "pdf",
                      "file_url": "https://storage.printfriendly.com/data-production/pdf_1760380000000_a1b2c3Letter.pdf",
                      "expires_at": "2026-10-15T12:00:00.000Z",
                      "size_bytes": 184233,
                      "document": {
                        "title": "Eclipse",
                        "url": "https://en.wikipedia.org/wiki/Eclipse",
                        "site": "en.wikipedia.org",
                        "word_count": 6120
                      },
                      "usage": {
                        "used_this_period": 3,
                        "included": 100,
                        "plan": "free"
                      }
                    }
                  },
                  "markdown": {
                    "summary": "URL to Markdown",
                    "value": {
                      "status": "success",
                      "output": "markdown",
                      "content": "# Eclipse\n\nAn eclipse is an astronomical event...",
                      "document": {
                        "title": "Eclipse",
                        "url": "https://en.wikipedia.org/wiki/Eclipse",
                        "site": "en.wikipedia.org",
                        "word_count": 6120
                      },
                      "usage": {
                        "used_this_period": 4,
                        "included": 100,
                        "plan": "free"
                      }
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Only with async: true. The job was queued. Nothing has been converted, counted or recorded yet: that happens when it runs, and only if it succeeds. Read the result at status_url.",
            "headers": {
              "Location": {
                "description": "Same as status_url.",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "X-Request-Id": {
                "description": "Quote this to support.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertJobAccepted"
                },
                "example": {
                  "status": "queued",
                  "job_id": "5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                  "status_url": "https://api.printfriendly.com/v2/jobs/5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                  "expires_at": "2026-10-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v2/pdf/create": {
      "post": {
        "operationId": "createPdf",
        "summary": "Create a PDF",
        "description": "Original endpoint. Unchanged.",
        "tags": [
          "Legacy"
        ],
        "requestBody": {
          "required": true,
          "description": "Send page_url or html. The same names work in the query string.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON with a file_url. With output_type set, or Basic auth, the file itself instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyFileResponse"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "get": {
        "operationId": "createPdfGet",
        "summary": "Create a PDF (query string)",
        "description": "Same as POST, with the parameters in the query string.",
        "tags": [
          "Legacy"
        ],
        "parameters": [
          {
            "name": "page_url",
            "in": "query",
            "required": false,
            "description": "The web page to convert. Send page_url or html.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "html",
            "in": "query",
            "required": false,
            "description": "HTML to convert. Used as sent. Up to 50 MB.",
            "schema": {
              "type": "string",
              "maxLength": 50000000
            }
          },
          {
            "name": "output_type",
            "in": "query",
            "required": false,
            "description": "Set it to get the file itself back instead of JSON with a file_url.",
            "schema": {
              "type": "string",
              "enum": [
                "attachment",
                "download",
                "inline"
              ]
            }
          },
          {
            "name": "css_url",
            "in": "query",
            "required": false,
            "description": "A stylesheet applied after ours.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "header_image_url",
            "in": "query",
            "required": false,
            "description": "An image shown above the title.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "header_text",
            "in": "query",
            "required": false,
            "description": "A line of text shown above the title.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Paper size for PDF output.",
            "schema": {
              "type": "string",
              "enum": [
                "Letter",
                "A4"
              ],
              "default": "Letter"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "Title for html input.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Language of the content.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "description": "Text direction.",
            "schema": {
              "type": "string",
              "enum": [
                "ltr",
                "rtl"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON with a file_url.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyFileResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v2/html/create": {
      "post": {
        "operationId": "createHtml",
        "summary": "Create a printable HTML page",
        "description": "Original endpoint. Unchanged.",
        "tags": [
          "Legacy"
        ],
        "requestBody": {
          "required": true,
          "description": "Send page_url or html. The same names work in the query string.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON with a file_url. With output_type set, or Basic auth, the file itself instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyFileResponse"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "get": {
        "operationId": "createHtmlGet",
        "summary": "Create a printable HTML page (query string)",
        "description": "Same as POST, with the parameters in the query string.",
        "tags": [
          "Legacy"
        ],
        "parameters": [
          {
            "name": "page_url",
            "in": "query",
            "required": false,
            "description": "The web page to convert. Send page_url or html.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "html",
            "in": "query",
            "required": false,
            "description": "HTML to convert. Used as sent. Up to 50 MB.",
            "schema": {
              "type": "string",
              "maxLength": 50000000
            }
          },
          {
            "name": "output_type",
            "in": "query",
            "required": false,
            "description": "Set it to get the file itself back instead of JSON with a file_url.",
            "schema": {
              "type": "string",
              "enum": [
                "attachment",
                "download",
                "inline"
              ]
            }
          },
          {
            "name": "css_url",
            "in": "query",
            "required": false,
            "description": "A stylesheet applied after ours.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "header_image_url",
            "in": "query",
            "required": false,
            "description": "An image shown above the title.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "header_text",
            "in": "query",
            "required": false,
            "description": "A line of text shown above the title.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Paper size for PDF output.",
            "schema": {
              "type": "string",
              "enum": [
                "Letter",
                "A4"
              ],
              "default": "Letter"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "Title for html input.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Language of the content.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "description": "Text direction.",
            "schema": {
              "type": "string",
              "enum": [
                "ltr",
                "rtl"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON with a file_url.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyFileResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v2/text/create": {
      "post": {
        "operationId": "createText",
        "summary": "Create a plain text file",
        "description": "Original endpoint. Unchanged.",
        "tags": [
          "Legacy"
        ],
        "requestBody": {
          "required": true,
          "description": "Send page_url or html. The same names work in the query string.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/LegacyCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON with a file_url. With output_type set, or Basic auth, the file itself instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyFileResponse"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "get": {
        "operationId": "createTextGet",
        "summary": "Create a plain text file (query string)",
        "description": "Same as POST, with the parameters in the query string.",
        "tags": [
          "Legacy"
        ],
        "parameters": [
          {
            "name": "page_url",
            "in": "query",
            "required": false,
            "description": "The web page to convert. Send page_url or html.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "html",
            "in": "query",
            "required": false,
            "description": "HTML to convert. Used as sent. Up to 50 MB.",
            "schema": {
              "type": "string",
              "maxLength": 50000000
            }
          },
          {
            "name": "output_type",
            "in": "query",
            "required": false,
            "description": "Set it to get the file itself back instead of JSON with a file_url.",
            "schema": {
              "type": "string",
              "enum": [
                "attachment",
                "download",
                "inline"
              ]
            }
          },
          {
            "name": "css_url",
            "in": "query",
            "required": false,
            "description": "A stylesheet applied after ours.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "header_image_url",
            "in": "query",
            "required": false,
            "description": "An image shown above the title.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "header_text",
            "in": "query",
            "required": false,
            "description": "A line of text shown above the title.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Paper size for PDF output.",
            "schema": {
              "type": "string",
              "enum": [
                "Letter",
                "A4"
              ],
              "default": "Letter"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "Title for html input.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Language of the content.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "description": "Text direction.",
            "schema": {
              "type": "string",
              "enum": [
                "ltr",
                "rtl"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON with a file_url.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyFileResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v2/jobs/{id}": {
      "get": {
        "operationId": "getJob",
        "summary": "Read an async conversion job",
        "description": "Where a job started with `async: true` stands and, once it has finished, its result. Only the key that started a job can read it; for any other key, and after 48 hours, the answer is 404 job_not_found. Polling has its own limit (120 a minute on the free plan, 600 on paid plans), separate from the limit on starting work, and is not blocked when a free key has used its month. While the job is unfinished the answer carries a Retry-After header; look again after that many seconds.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The job_id from the 202 answer.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job. status says whether to look again: queued and processing are not final, succeeded and failed are and never change.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before looking again. Only while the job is unfinished.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                },
                "examples": {
                  "queued": {
                    "summary": "Waiting for a worker",
                    "value": {
                      "status": "queued",
                      "job_id": "5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                      "created_at": "2026-10-13T12:00:00.000Z"
                    }
                  },
                  "processing": {
                    "summary": "Being converted",
                    "value": {
                      "status": "processing",
                      "job_id": "5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                      "created_at": "2026-10-13T12:00:00.000Z"
                    }
                  },
                  "succeeded": {
                    "summary": "Done: result is the synchronous 200 body",
                    "value": {
                      "status": "succeeded",
                      "job_id": "5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                      "created_at": "2026-10-13T12:00:00.000Z",
                      "finished_at": "2026-10-13T12:00:07.412Z",
                      "result": {
                        "status": "success",
                        "output": "pdf",
                        "file_url": "https://storage.printfriendly.com/data-production/pdf_1760380000000_a1b2c3Letter.pdf",
                        "expires_at": "2026-10-15T12:00:00.000Z",
                        "size_bytes": 184233,
                        "document": {
                          "title": "Eclipse",
                          "url": "https://en.wikipedia.org/wiki/Eclipse",
                          "site": "en.wikipedia.org",
                          "word_count": 6120
                        },
                        "usage": {
                          "used_this_period": 3,
                          "included": 100,
                          "plan": "free"
                        }
                      }
                    }
                  },
                  "failed": {
                    "summary": "Did not work: error is the synchronous error object",
                    "value": {
                      "status": "failed",
                      "job_id": "5b1f6a52-0c0e-4d8e-9d8f-3f0a6a7d1c21",
                      "created_at": "2026-10-13T12:00:00.000Z",
                      "finished_at": "2026-10-13T12:00:04.118Z",
                      "error": {
                        "code": "fetch_failed",
                        "message": "Print technology timed-out while trying to connect to this page",
                        "retryable": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "x-error-codes": [
    {
      "code": "invalid_parameters",
      "http_status": [
        400,
        422
      ],
      "retryable": false,
      "description": "A parameter is missing, malformed or not allowed. error.errors names each one."
    },
    {
      "code": "api_key_required",
      "http_status": [
        401
      ],
      "retryable": false,
      "description": "No API key was sent."
    },
    {
      "code": "invalid_api_key",
      "http_status": [
        401
      ],
      "retryable": false,
      "description": "The API key is not known."
    },
    {
      "code": "subscription_inactive",
      "http_status": [
        401
      ],
      "retryable": false,
      "description": "The subscription behind the key is not active."
    },
    {
      "code": "limit_exceeded",
      "http_status": [
        401
      ],
      "retryable": false,
      "description": "The key has reached the limit set on it."
    },
    {
      "code": "quota_exceeded",
      "http_status": [
        402
      ],
      "retryable": false,
      "description": "The free plan has no documents left. error.upgrade_url says where to upgrade."
    },
    {
      "code": "rate_limited",
      "http_status": [
        429
      ],
      "retryable": true,
      "description": "Too many requests. Wait Retry-After seconds."
    },
    {
      "code": "fetch_failed",
      "http_status": [
        422
      ],
      "retryable": true,
      "description": "The page could not be loaded: down, slow or blocking us."
    },
    {
      "code": "parse_failed",
      "http_status": [
        422
      ],
      "retryable": false,
      "description": "The page loaded but no article could be found in it."
    },
    {
      "code": "render_timeout",
      "http_status": [
        422
      ],
      "retryable": true,
      "description": "The conversion did not finish inside the 27 second ceiling."
    },
    {
      "code": "render_failed",
      "http_status": [
        422
      ],
      "retryable": true,
      "description": "The conversion failed on our side."
    },
    {
      "code": "html_too_large",
      "http_status": [
        400,
        413,
        422
      ],
      "retryable": false,
      "description": "The html has more than 2 MB of markup or is over 50 MB, or a fetched page was too big to process."
    },
    {
      "code": "homepage_url",
      "http_status": [
        422
      ],
      "retryable": false,
      "description": "The address is a site's front page, not an article."
    },
    {
      "code": "content_blocked",
      "http_status": [
        422
      ],
      "retryable": false,
      "description": "Returned only when content checks are enabled for the key's plan. The page failed the same content checks printfriendly.com applies under the Acceptable Use Policy (https://www.printfriendly.com/acceptable-use-policy). The same page fails every time."
    },
    {
      "code": "job_not_found",
      "http_status": [
        404
      ],
      "retryable": false,
      "description": "GET /v2/jobs/{id} found no job for that id under this key: wrong id, another key's job, or older than 48 hours."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer YOUR_API_KEY. The recommended way."
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "The key in an X-API-Key header."
      },
      "apiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "api_key",
        "description": "The key as an api_key query parameter. It can end up in logs, so prefer a header."
      },
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic with the key as the username and an empty password. On the older /v2/*/create endpoints this returns the file itself instead of JSON."
      }
    },
    "schemas": {
      "ErrorCode": {
        "type": "string",
        "enum": [
          "invalid_parameters",
          "api_key_required",
          "invalid_api_key",
          "subscription_inactive",
          "limit_exceeded",
          "quota_exceeded",
          "rate_limited",
          "fetch_failed",
          "parse_failed",
          "render_timeout",
          "render_failed",
          "html_too_large",
          "homepage_url",
          "content_blocked",
          "job_not_found"
        ],
        "description": "Machine-readable reason a request failed. Branch on this, not on the message. HTTP status per code: invalid_parameters (400/422); api_key_required (401); invalid_api_key (401); subscription_inactive (401); limit_exceeded (401); quota_exceeded (402); rate_limited (429); fetch_failed (422); parse_failed (422); render_timeout (422); render_failed (422); html_too_large (400/413/422); homepage_url (422); content_blocked (422); job_not_found (404)."
      },
      "ConvertRequest": {
        "type": "object",
        "description": "Send exactly one of url, html or markdown.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The web page to convert. We fetch it and keep the article. Send exactly one of url, html or markdown."
          },
          "page_url": {
            "type": "string",
            "format": "uri",
            "deprecated": true,
            "description": "Legacy name for url, accepted so /v2/pdf/create callers can switch by changing the path."
          },
          "html": {
            "type": "string",
            "maxLength": 50000000,
            "description": "HTML to convert, treated as the article body. Scripts and anything unsafe are removed. Up to 2 MB of markup; embedded data: images do not count toward that, and 50 MB is the limit in all."
          },
          "markdown": {
            "type": "string",
            "maxLength": 1000000,
            "description": "Markdown to convert (GitHub flavour). Up to 1 MB."
          },
          "source_url": {
            "type": "string",
            "format": "uri",
            "description": "With html only. The address the HTML came from. It appears under the title and resolves relative links."
          },
          "title": {
            "type": "string",
            "maxLength": 500,
            "description": "Heading printed above html or markdown. Ignored for url, which uses the page's own title."
          },
          "output": {
            "type": "string",
            "enum": [
              "pdf",
              "docx",
              "markdown",
              "json",
              "html",
              "text"
            ],
            "default": "pdf",
            "description": "pdf and docx return a file_url. markdown, html and text return content. json returns the whole article in document."
          },
          "page_size": {
            "type": "string",
            "enum": [
              "letter",
              "a4"
            ],
            "default": "letter",
            "description": "Paper size for pdf and docx. Case does not matter."
          },
          "css_url": {
            "type": "string",
            "format": "uri",
            "description": "A stylesheet applied after ours. pdf and html output."
          },
          "custom_css_url": {
            "type": "string",
            "format": "uri",
            "deprecated": true,
            "description": "Legacy name for css_url."
          },
          "header_text": {
            "type": "string",
            "maxLength": 500,
            "description": "A line of text shown above the title. pdf and html output."
          },
          "header_image_url": {
            "type": "string",
            "format": "uri",
            "description": "An image shown above the title, such as a logo. pdf and html output."
          },
          "lang": {
            "type": "string",
            "examples": [
              "en",
              "pt-BR"
            ],
            "description": "Language of the content. Defaults to the page's own."
          },
          "dir": {
            "type": "string",
            "enum": [
              "ltr",
              "rtl"
            ],
            "description": "Text direction. Defaults to the page's own."
          },
          "include_links": {
            "type": "boolean",
            "default": false,
            "description": "Keep link and image addresses in markdown and text output. Off by default: links keep their words and lose their addresses."
          },
          "async": {
            "type": "boolean",
            "default": false,
            "description": "Run the conversion in the background. The answer is 202 with a job_id and a status_url straight away; nothing is converted or counted until the job runs. Read the result with GET /v2/jobs/{id} or give a webhook_url. The result is exactly the body a synchronous call returns."
          },
          "mode": {
            "type": "string",
            "enum": [
              "sync",
              "async"
            ],
            "default": "sync",
            "description": "Another way to write async: \"async\" is async: true. If you send both they must agree, otherwise the request is a 400."
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "pattern": "^https://",
            "description": "Only with async. An https address on a public host that we POST the finished job to, once, as JSON (the same object GET /v2/jobs/{id} returns). Signed in X-PrintFriendly-Signature. Addresses that point at a private or internal network are refused with a 400. Delivery is retried three times and is best effort: the result is always readable at status_url."
          }
        },
        "oneOf": [
          {
            "required": [
              "url"
            ]
          },
          {
            "required": [
              "page_url"
            ]
          },
          {
            "required": [
              "html"
            ]
          },
          {
            "required": [
              "markdown"
            ]
          }
        ]
      },
      "LegacyCreateRequest": {
        "type": "object",
        "properties": {
          "page_url": {
            "type": "string",
            "format": "uri",
            "description": "The web page to convert. Send page_url or html."
          },
          "html": {
            "type": "string",
            "maxLength": 50000000,
            "description": "HTML to convert. Used as sent. Up to 50 MB."
          },
          "output_type": {
            "type": "string",
            "enum": [
              "attachment",
              "download",
              "inline"
            ],
            "description": "Set it to get the file itself back instead of JSON with a file_url."
          },
          "css_url": {
            "type": "string",
            "format": "uri",
            "description": "A stylesheet applied after ours."
          },
          "header_image_url": {
            "type": "string",
            "format": "uri",
            "description": "An image shown above the title."
          },
          "header_text": {
            "type": "string",
            "description": "A line of text shown above the title."
          },
          "page_size": {
            "type": "string",
            "enum": [
              "Letter",
              "A4"
            ],
            "default": "Letter",
            "description": "Paper size for PDF output."
          },
          "title": {
            "type": "string",
            "description": "Title for html input."
          },
          "lang": {
            "type": "string",
            "description": "Language of the content."
          },
          "dir": {
            "type": "string",
            "enum": [
              "ltr",
              "rtl"
            ],
            "description": "Text direction."
          }
        },
        "anyOf": [
          {
            "required": [
              "page_url"
            ]
          },
          {
            "required": [
              "html"
            ]
          }
        ]
      },
      "ArticleMeta": {
        "type": "object",
        "required": [
          "title",
          "word_count"
        ],
        "properties": {
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "site": {
            "type": "string",
            "description": "Hostname of the page."
          },
          "author": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "description": "ISO 8601, when known."
          },
          "excerpt": {
            "type": "string"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "description": "Main image of the article."
          },
          "word_count": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "ArticleDocument": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ArticleMeta"
          },
          {
            "type": "object",
            "required": [
              "content_html",
              "content_markdown"
            ],
            "properties": {
              "content_html": {
                "type": "string",
                "description": "Clean article HTML."
              },
              "content_markdown": {
                "type": "string",
                "description": "The article as Markdown."
              }
            }
          }
        ]
      },
      "Usage": {
        "type": "object",
        "required": [
          "used_this_period",
          "included",
          "plan"
        ],
        "properties": {
          "used_this_period": {
            "type": "integer",
            "description": "Documents used so far this period, counting this one."
          },
          "included": {
            "type": "integer",
            "description": "Documents the plan includes."
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "paid"
            ]
          }
        }
      },
      "ConvertFileResponse": {
        "type": "object",
        "required": [
          "status",
          "output",
          "file_url",
          "expires_at",
          "size_bytes",
          "document",
          "usage"
        ],
        "properties": {
          "status": {
            "const": "success"
          },
          "output": {
            "type": "string",
            "enum": [
              "pdf",
              "docx"
            ]
          },
          "file_url": {
            "type": "string",
            "format": "uri",
            "description": "Public link. Valid for 48 hours."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "size_bytes": {
            "type": "integer"
          },
          "document": {
            "$ref": "#/components/schemas/ArticleMeta"
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        }
      },
      "ConvertContentResponse": {
        "type": "object",
        "required": [
          "status",
          "output",
          "content",
          "document",
          "usage"
        ],
        "properties": {
          "status": {
            "const": "success"
          },
          "output": {
            "type": "string",
            "enum": [
              "markdown",
              "html",
              "text"
            ]
          },
          "content": {
            "type": "string"
          },
          "document": {
            "$ref": "#/components/schemas/ArticleMeta"
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        }
      },
      "ConvertJsonResponse": {
        "type": "object",
        "required": [
          "status",
          "output",
          "document",
          "usage"
        ],
        "properties": {
          "status": {
            "const": "success"
          },
          "output": {
            "const": "json"
          },
          "document": {
            "$ref": "#/components/schemas/ArticleDocument"
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        }
      },
      "LegacyFileResponse": {
        "type": "object",
        "required": [
          "status",
          "file_url"
        ],
        "properties": {
          "status": {
            "const": "success"
          },
          "file_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "GenerationError": {
        "type": "object",
        "description": "A well-formed request that could not be completed.",
        "required": [
          "status",
          "message",
          "error"
        ],
        "properties": {
          "status": {
            "const": "failed"
          },
          "message": {
            "type": "string"
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether sending the same request again can work."
              },
              "upgrade_url": {
                "type": "string",
                "format": "uri",
                "description": "Only with quota_exceeded."
              },
              "retry_after_seconds": {
                "type": "integer",
                "description": "Only with rate_limited."
              }
            }
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "description": "A request with a bad key or bad parameters.",
        "required": [
          "error"
        ],
        "properties": {
          "status": {
            "const": "failed"
          },
          "error": {
            "type": "object",
            "required": [
              "type",
              "errors",
              "message",
              "code",
              "retryable"
            ],
            "properties": {
              "type": {
                "const": "parameters-validation"
              },
              "errors": {
                "type": "object",
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "description": "Parameter name to the problems with it."
              },
              "message": {
                "type": "string"
              },
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "retryable": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "ConvertJobAccepted": {
        "type": "object",
        "required": [
          "status",
          "job_id",
          "status_url",
          "expires_at"
        ],
        "properties": {
          "status": {
            "const": "queued"
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "status_url": {
            "type": "string",
            "format": "uri",
            "description": "GET this with the same key to read the job."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the job and its result are deleted: 48 hours after it was started."
          }
        }
      },
      "Job": {
        "type": "object",
        "description": "An async conversion job. Also the body of the webhook.",
        "required": [
          "status",
          "job_id",
          "created_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "succeeded",
              "failed"
            ],
            "description": "queued: waiting for a worker. processing: being converted. succeeded and failed are final."
          },
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "description": "Only when status is succeeded or failed."
          },
          "result": {
            "description": "Only when status is succeeded. Exactly the body POST /v2/convert answers 200 with, usage included.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/ConvertFileResponse"
              },
              {
                "$ref": "#/components/schemas/ConvertContentResponse"
              },
              {
                "$ref": "#/components/schemas/ConvertJsonResponse"
              }
            ]
          },
          "error": {
            "description": "Only when status is failed. The same object a failed synchronous call carries as error.",
            "type": "object",
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether starting the same job again can work."
              },
              "upgrade_url": {
                "type": "string",
                "format": "uri",
                "description": "Only with quota_exceeded."
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "A parameter is missing or wrong: invalid_parameters, or html_too_large.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "example": {
              "status": "failed",
              "error": {
                "type": "parameters-validation",
                "errors": {
                  "base": [
                    "You must provide one of url, html or markdown"
                  ]
                },
                "message": "You must provide one of url, html or markdown",
                "code": "invalid_parameters",
                "retryable": false
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The key is missing or not accepted: api_key_required, invalid_api_key, subscription_inactive or limit_exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "QuotaExceeded": {
        "description": "The free plan has no documents left this month: quota_exceeded, with upgrade_url.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenerationError"
            },
            "example": {
              "status": "failed",
              "message": "Your free plan includes 100 documents a month. Upgrade for more.",
              "error": {
                "code": "quota_exceeded",
                "message": "Your free plan includes 100 documents a month. Upgrade for more.",
                "upgrade_url": "https://www.printfriendly.com/api#pricing",
                "retryable": false
              }
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request body is over the 50 MB limit: html_too_large.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "Unprocessable": {
        "description": "The request was fine but the conversion failed: fetch_failed, parse_failed, homepage_url, content_blocked (only when content checks are enabled for your plan), render_timeout or render_failed. Check error.retryable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenerationError"
            },
            "example": {
              "status": "failed",
              "message": "Print technology timed-out while trying to connect to this page",
              "error": {
                "code": "fetch_failed",
                "message": "Print technology timed-out while trying to connect to this page",
                "retryable": true
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Too many requests: rate_limited. For POST /v2/convert with async: true it also means the key already has the most unfinished jobs it may (3 on the free plan, 20 on paid plans) or the service has too many waiting. Check Retry-After.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenerationError"
            }
          }
        }
      },
      "NotFound": {
        "description": "No job with that id for this key: job_not_found. The id may be wrong, belong to another key, or be older than 48 hours.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenerationError"
            },
            "example": {
              "status": "failed",
              "message": "No job with that id for this key. Jobs are kept for 48 hours after they are created.",
              "error": {
                "code": "job_not_found",
                "message": "No job with that id for this key. Jobs are kept for 48 hours after they are created.",
                "retryable": false
              }
            }
          }
        }
      }
    }
  }
}
