{
  "openapi": "3.1.1",
  "info": {
    "title": "Searchable PDF OCR API",
    "version": "1.0.0",
    "summary": "Make scanned PDFs searchable and copyable with OCRmyPDF and Tesseract, optionally as PDF/A.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://searchablepdfocr.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Make scanned PDFs searchable and copyable with OCRmyPDF and Tesseract, optionally as PDF/A.",
        "description": "Adds an invisible, selectable text layer to scanned PDFs (or a scanned JPG/PNG) using OCRmyPDF and Tesseract. Choose the document languages, straighten crooked scans, fix upside-down pages and optionally create a PDF/A for archiving. Reports how many pages were recognised and shows a sample of the extracted text.\n\nMethodology:\n- Images (JPG/PNG/TIFF) are first wrapped into a PDF with img2pdf. PDFs are validated with qpdf and decrypted with your password if needed.\n- OCRmyPDF renders each page, lets Tesseract recognise the text in the chosen languages and lays the words as invisible text exactly over the image, so the page looks unchanged but can be searched and copied.\n- \"Leave them as they are\" skips pages that already have text (born-digital pages stay untouched). \"OCR every page\" rasterises such pages too, which replaces their vector text with an image plus OCR text.\n- Pages processed = pages without extractable text before (or all pages when forcing OCR). The text sample and character count are read back from the result with pdftotext.\n- Without OCRmyPDF the server falls back to Tesseract alone: every page is rendered at 300 DPI in grayscale and rebuilt as image + text (deskew, rotation and PDF/A are then unavailable).\n- Random internal names, dates and IDs are normalised so the same input gives the same file; PDF/A keeps its XMP metadata with the timestamps set to 2000-01-01.\n\nOCR is not perfect: handwriting, low-resolution scans, unusual fonts and complex layouts produce errors. Check important text against the image.",
        "tags": [
          "documents"
        ],
        "x-mcp-tool": "searchable_pdf_ocr",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchablePdfOcrInput"
              },
              "examples": {
                "example1": {
                  "summary": "Scanned line of text (PNG) to a searchable PDF",
                  "value": {
                    "file": {
                      "filename": "scan.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAAggAAABGAQAAAACBSlH7AAABvklEQVRYw+3UMUvEMBQH8MgVM+bGG4QMfgkn47dx1V2aA4eO/UonGfoxbMngaLpFCHm+l1ZUUNOeIAh5w5+jvfy45N4Lg98WK0IRilCEIvydYL/+RtDw9LMQ1WchcnAKI4hjBXcCvcBwm2OFnukDx+jXC2+Q102HYd4frRQgKGEx2hXCmexA9trCCRzoKJW0GHiQ4UZ7GfQhK2y5iWovbWCaqSSMFChcyQMPen+dExi3UVXC+m1d4bp4mYSdhFCJh/ugK54TuLG4qrVu2n24VCN+2mJLyPZxCLVscoKwY6hVZ3GdJqEmoWYbZBqgV1lBonCnOzuAhw7Ak+BrbhDs4IVeZYWRBGPtJLiIgotiSIJfJngftCWB4aENJAxR0qY6x44QDAlmEmxPQrYfUHCzQI8aEppZMAt3EW4/CIIWC5h20a4QzCxEiYtTrBHieZj+TU2DBdjg6k1wS4U4dxSQgg2uoR2oo1BY0lFj3MS5q5/B4Vl2FIa6uumXdDX+Xg7TZJ0KvKMU3VEYabLYkslKwjzdSehFCprufrdkupMw3zCKBKcwvKQbxl3kb5gfig42W0X4J8KiKkIRilCE7+oV25/CiYnY+pIAAAAASUVORK5CYII="
                    },
                    "languages": [
                      "eng"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchablePdfOcrResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "SearchablePdfOcrInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "file": {
            "title": "Scanned PDF or image",
            "description": "Max 100 MB and 60 pages. Processed on our server and deleted; the result can be downloaded for about 2 hours. Upload as {filename, contentType?, contentBase64}. Max 102400 KiB. Accepts .pdf,.jpg,.jpeg,.png,.tif,.tiff,application/pdf,image/jpeg,image/png,image/tiff.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "languages": {
            "title": "Document languages",
            "description": "Pick every language that appears. Only languages installed on the server can be used; the result lists them.",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "eng",
                "deu",
                "fra",
                "spa",
                "ita",
                "nld",
                "por",
                "pol"
              ]
            },
            "default": [
              "eng"
            ]
          },
          "mode": {
            "title": "Pages that already contain text",
            "type": "string",
            "enum": [
              "skip-text",
              "force-ocr"
            ],
            "default": "skip-text"
          },
          "deskew": {
            "title": "Straighten crooked scans (deskew)",
            "type": "boolean",
            "default": false
          },
          "rotatePages": {
            "title": "Fix sideways or upside-down pages",
            "type": "boolean",
            "default": false
          },
          "pdfa": {
            "title": "Save as PDF/A-2b (archiving)",
            "type": "boolean",
            "default": false
          },
          "password": {
            "title": "PDF password (only if the file is protected)",
            "description": "Used only to open the file for this conversion; it is written to a private temporary file, never to a command line, and deleted with the job.",
            "type": "string",
            "maxLength": 200
          }
        },
        "required": [
          "file"
        ]
      },
      "SearchablePdfOcrResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "engine",
              "languages",
              "installedLanguages",
              "pages",
              "pagesOcrd",
              "pagesWithTextBefore",
              "charactersAfter",
              "textSample",
              "outputBytes",
              "pdfa",
              "artifact"
            ],
            "properties": {
              "engine": {
                "type": "string",
                "enum": [
                  "ocrmypdf",
                  "tesseract-fallback"
                ]
              },
              "languages": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "installedLanguages": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "pages": {
                "type": "integer"
              },
              "pagesOcrd": {
                "type": "integer",
                "description": "Pages that went through OCR."
              },
              "pagesWithTextBefore": {
                "type": "integer"
              },
              "charactersAfter": {
                "type": "integer",
                "description": "Non-space characters of extractable text in the result."
              },
              "textSample": {
                "type": "string"
              },
              "outputBytes": {
                "type": "integer"
              },
              "pdfa": {
                "type": "boolean"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "preview": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}