{
  "openapi": "3.1.0",
  "info": {
    "title": "pdfToMarkdown API",
    "version": "1.0.0",
    "summary": "Convert any PDF to clean, structured markdown with one API call.",
    "description": "REST API that converts PDF files (including scanned/image-based PDFs, complex tables, multi-column layouts, math, and CJK text) to markdown using a vision-language OCR model.\n\n**Try it without signing up:** use the public demo key `demo_public_key` as the Bearer token. The Hacker tier processes page 1 only, allows 3 requests per minute per IP, and adds a small watermark footer.\n\n**Free Developer tier:** sign in with GitHub at https://pdftomarkdown.dev/auth/github to get a personal API key with 100 pages/month, full multi-page support, and no watermark.\n\n**Latency:** OCR runs on GPUs at roughly 10-30 seconds per page; the first request after a quiet period can add a cold-start wait. Set your HTTP client timeout to at least 10 minutes for large documents, or use `input.max_pages`.\n\n**CLI:** the same API is available as a zero-install command: `npx pdftomarkdown <file-or-url>` (Node 18+, key via `PDFTOMARKDOWN_API_KEY`) — guide at https://pdftomarkdown.dev/docs/cli/.",
    "contact": {
      "url": "https://pdftomarkdown.dev/contact/"
    }
  },
  "servers": [
    {
      "url": "https://pdftomarkdown.dev"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/convert": {
      "post": {
        "operationId": "convertPdfToMarkdown",
        "summary": "Convert a PDF to markdown",
        "description": "Provide exactly one of `input.pdf_url` (a publicly reachable http(s) URL) or `input.pdf_base64` (the base64-encoded file contents). The call is synchronous: the response contains the finished markdown.\n\nNo-signup example:\n```\ncurl -X POST https://pdftomarkdown.dev/v1/convert \\\n  -H \"Authorization: Bearer demo_public_key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"input\":{\"pdf_url\":\"https://pdftomarkdown.dev/samples/invoice.pdf\"}}'\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConvertRequest"
              },
              "examples": {
                "fromUrl": {
                  "summary": "Convert a PDF by URL",
                  "value": {
                    "input": {
                      "pdf_url": "https://pdftomarkdown.dev/samples/invoice.pdf"
                    }
                  }
                },
                "fromBase64": {
                  "summary": "Convert a local file (base64)",
                  "value": {
                    "input": {
                      "pdf_base64": "JVBERi0xLjcKJ..."
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conversion succeeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "x-queue-ms": {
                "description": "Milliseconds the job waited for a GPU worker before OCR started. High values indicate a cold start.",
                "schema": {
                  "type": "integer"
                }
              },
              "x-processing-ms": {
                "description": "Milliseconds of actual OCR processing time on the GPU.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-PdfToMarkdown-Page-Cap": {
                "description": "Present on Hacker-tier responses: the page cap that was applied (always 1).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertResponse"
                },
                "example": {
                  "markdown": "# Invoice\n\nDate: 2024-01-15\n\n| Item | Qty | Price |\n|---|---|---|\n| API Pro Plan | 1 | $49.00 |",
                  "pages": 3,
                  "request_id": "req_abc123"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request: body is not valid JSON, or `input.pdf_url`/`input.pdf_base64` is missing, duplicated, or badly formatted (error codes: `invalid_request`, `upstream_invalid_request`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "Provide exactly one of input.pdf_url or input.pdf_base64. Example: {\"input\":{\"pdf_url\":\"https://pdftomarkdown.dev/samples/invoice.pdf\"}} or {\"input\":{\"pdf_base64\":\"<base64>\"}}.",
                  "docs": "https://pdftomarkdown.dev/docs/",
                  "request_id": "req_bad123"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (error codes: `missing_api_key`, `invalid_api_key`, `invalid_authorization_header`). Get a free key at https://pdftomarkdown.dev/auth/github.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "missing_api_key",
                  "message": "Send an Authorization: Bearer <api_key> header. Get a free key in 30 seconds at https://pdftomarkdown.dev/auth/github.",
                  "docs": "https://pdftomarkdown.dev/docs/",
                  "request_id": "req_auth123"
                }
              }
            }
          },
          "422": {
            "description": "The source PDF could not be used: unreachable or private `pdf_url`, TLS failure, or the file is not a parseable PDF (error codes: `invalid_source_url`, `source_fetch_failed`, `source_ssl_error`, `unprocessable_document`). For private/local files, send `input.pdf_base64` instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or quota reached. Hacker tier: `rate_limited`, 3 requests per minute per IP — retry after `retry_after_seconds`, or get a free Developer key with higher limits at https://pdftomarkdown.dev/auth/github. Developer tier: `quota_exceeded`, monthly page quota exhausted — resets at `reset_at`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "rate_limited",
                  "message": "Free Hacker-tier limit reached (3 requests per minute per IP). Retry shortly, or get a free API key with higher limits at https://pdftomarkdown.dev/auth/github.",
                  "docs": "https://pdftomarkdown.dev/docs/",
                  "request_id": "req_rate123",
                  "retry_after_seconds": 42
                }
              }
            }
          },
          "502": {
            "description": "The upstream OCR worker was unreachable or failed while processing (error codes: `upstream_unreachable`, `upstream_invalid_response`, `upstream_processing_failed`). Retry; contact support with the request_id if it persists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "OCR exceeded the processing time limit (error code: `upstream_timeout`). Expect roughly 10-30 seconds per page; for large documents set `input.max_pages` or split the PDF, and set your HTTP client timeout to at least 10 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key as a Bearer token. Use `demo_public_key` for instant no-signup access (page 1 only, 3 req/min per IP), or get a free personal key at https://pdftomarkdown.dev/auth/github (100 pages/month)."
      }
    },
    "headers": {
      "XRequestId": {
        "description": "Unique request ID. Include it when contacting support.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ConvertRequest": {
        "type": "object",
        "required": ["input"],
        "properties": {
          "input": {
            "type": "object",
            "description": "Provide exactly one of `pdf_url` or `pdf_base64`.",
            "properties": {
              "pdf_url": {
                "type": "string",
                "format": "uri",
                "description": "Publicly reachable http(s) URL of the PDF. If the URL is private, behind a login, or bot-protected, download the file and use pdf_base64 instead."
              },
              "pdf_base64": {
                "type": "string",
                "contentEncoding": "base64",
                "description": "Base64-encoded contents of the PDF file."
              },
              "max_pages": {
                "type": "integer",
                "minimum": 1,
                "description": "Process at most this many pages. Useful to bound latency on large documents. The Hacker tier is always capped at 1."
              },
              "include_raw": {
                "type": "boolean",
                "default": false,
                "description": "If true, the response includes an extra `raw` field with model internals for debugging."
              }
            }
          }
        }
      },
      "ConvertResponse": {
        "type": "object",
        "required": ["markdown", "pages", "request_id"],
        "properties": {
          "markdown": {
            "type": "string",
            "description": "The document converted to markdown. Tables, headings, and multi-column layouts are preserved."
          },
          "pages": {
            "type": "integer",
            "description": "Number of pages processed (and billed against the monthly quota on the Developer tier)."
          },
          "request_id": {
            "type": "string"
          },
          "raw": {
            "description": "Model internals, present only when input.include_raw=true."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error", "message", "request_id"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code."
          },
          "message": {
            "type": "string",
            "description": "Human/agent-readable explanation with the recommended fix."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Link to the API documentation."
          },
          "request_id": {
            "type": "string"
          },
          "retry_after_seconds": {
            "type": "integer",
            "description": "Present on 429 responses: seconds until the limit resets."
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present on quota_exceeded responses: when the monthly quota resets."
          }
        }
      }
    }
  }
}
