Converting scanned PDFs to Markdown in Python lets you combine document recognition, downloading, and text processing into a single workflow. PDF Craft handles conversions in the cloud: your Python script creates a task, uploads the PDF, waits for processing, and downloads a ZIP containing Markdown and image assets.
Start with the official Python example. It uses a fixed one-page PDF to verify connectivity, authentication, and output downloads. To process your own documents, you will need to update the upload source and output validation. If you only need to convert a file manually, see the web conversion guide instead.
Run the fixed sample first
You need Python 3.9 or later, with no third-party dependencies. Create a production API key in the OOMOL Console and confirm that your account has enough credits. Running conversions incurs usage charges; see the current rates.
The following commands work in Bash and Zsh. Download the script, then enter your key without printing it to the terminal:
curl --fail --show-error --location \
https://pdfcraft.ai/examples/api-quickstart.py \
--output api-quickstart.py
export PDF_CRAFT_API_KEY="$(
python3 -c 'import getpass; print(getpass.getpass("API key: "))'
)"
python3 api-quickstart.py
The script saves a state file named pdf-craft-task.json. Its uuid identifies the conversion so you can return to the same task later. When the run succeeds, the output is written to pdf-craft-result.zip. The script also checks the generated Markdown for a known sentence from the sample PDF to confirm it received the expected output.
This script only processes the built-in sample and does not include an --input option. To convert your own files, update the upload source and result validation as described below.
Save the task ID and continue according to its status
Conversion involves distinct stages: create, upload, launch, query, and download. An accepted launch request means the service has accepted the task; you retrieve the result once processing finishes.
The script's main loop determines its next action based on the task status:
| Status | What the program does next |
|---|---|
waiting_for_upload | Requests upload URLs, uploads if needed, and submits conversion parameters |
running | Waits and queries again, gradually increasing the polling interval |
succeeded | Requests a download URL, downloads the ZIP, and saves it |
failed or cancelled | Stops and preserves the error information |
If an upload has already finished, requesting upload URLs again returns read URLs for the file, so you do not need to re-upload it. Launching with {"output_format":"markdown"} and omitting a target language preserves the document's original language. If you need footnotes, configure refinement in your own code using the available conversion parameters.
Send your API key only with API requests. Do not include it when uploading to or downloading from the storage URLs returned by the service. Upload requests must include the headers provided in the response. The step-by-step cURL guide shows the complete request sequence.
Resume the same task after a timeout
A local timeout or connection failure does not necessarily mean the cloud conversion failed. Keep the state file and resume using the same account that created the task:
python3 api-quickstart.py \
--resume pdf-craft-task.json \
--output pdf-craft-result.zip \
--timeout 1200
The --timeout flag sets the total wait time for this script execution, in seconds. It does not reset the processing timer for the cloud task. The script will not overwrite an existing output file, so specify a different --output path if needed.
If the state file contains creation_unknown without a uuid, check your task list to confirm whether creation succeeded before creating another task. Relaunching a task with the failed status will not run it again. See the task lifecycle guide for recovery steps at each stage.
Read the Markdown inside the ZIP with Python
You can inspect the downloaded archive without extracting every file to disk. This snippet uses only the Python standard library to list archived assets and preview the beginning of each Markdown file:
from pathlib import Path
from zipfile import ZipFile
result = Path("pdf-craft-result.zip")
with ZipFile(result) as archive:
for item in archive.infolist():
if item.is_dir():
continue
print(f"{item.filename}: {item.file_size} bytes")
if item.filename.lower().endswith(".md"):
with archive.open(item) as source:
preview = source.read(8192).decode("utf-8", errors="replace")
print(preview)
This reads up to the first 8 KiB of each Markdown file. Longer documents will be truncated in the preview, and the final character may be incomplete. For downstream processing, read the full file contents according to your application's memory requirements and document sizes.
To edit or render the results locally, unpack the archive into a dedicated folder while preserving its directory structure. Moving only the .md file can break relative links to extracted images. Always keep the accompanying assets with the document when archiving or sharing it; see the Markdown image resource guide.
Adapt the workflow for your own PDF
Once the sample runs successfully, you can reuse the core task loop and adapt the input and validation logic for your own pipeline:
- Upload source: Replace the step that downloads
SAMPLE_URLwith code that reads your local PDF. For example,Path("document.pdf").read_bytes()provides the payload to send to the task's upload URL. Plan your upload strategy and memory usage around the size of your files. - Result validation: The sample's
EXPECTED_TEXTcheck applies only to its one-page PDF. Replace it with checks for your expected Markdown files, then validate key sections or content as needed. - Task records: Save the input file reference,
uuid, and launch parameters together. When resuming, use the same file and parameters. Replacing a file that has already been uploaded requires creating a new task. - Size and time limits: The example script caps each response read at 10 MiB and defaults to a 600-second timeout. These are settings in the example code, not general service file limits.
Start by testing with one of your actual documents. Save the source PDF, task record, and output ZIP before integrating the extraction step into downstream note systems or automated pipelines. This ensures you can resume after an interruption and inspect the generated Markdown against the original file.