NewConnect Claude, Gemini, ChatGPT, and other AI agents to API Ninjas via our MCP server

How to Convert a PDF to Text

Image to Text APIUpdated August 28, 2026
Create a free API key and it will be pre-filled into every code sample on this page. No credit card required.
To convert a PDF to text, extract its embedded text layer with a library like pypdf — and when the PDF is a scan with no text layer, render the pages to images and run OCR with the Image to Text API. Which route you need depends entirely on the file, so this guide starts by telling them apart, then walks each path.

Prerequisites

You need:

  • Python 3.8 or newer.
  • pypdf for digital PDFs; pypdfium2 and requests for the OCR route (all pure pip installs, no system packages).
  • For OCR only: a free API Ninjas API key — issued instantly, no credit card; existing accounts find it on the profile page.
shell
pip install pypdf pdfplumber pypdfium2 requests

Step 1: Identify your PDF type

Every PDF-to-text problem is one of two very different problems, and picking wrong is how you end up staring at empty output:

PDF typeHow to tellRight tool
DigitalText can be selected in a viewerpypdf / pdftotext — instant, free
ScannedPages are images; nothing selectsOCR via the Image to Text API
MixedSome pages select, some don'tExtract first, OCR pages that come back empty

In code, the test is how much text extraction returns:

detect.py
from pypdf import PdfReader reader = PdfReader('document.pdf') sample = ''.join((page.extract_text() or '') for page in reader.pages[:3]) if len(sample.strip()) < 50: print('Likely a scanned PDF — go to Step 3 (OCR)') else: print('Digital PDF — Step 2 will work')

Step 2: Extract the text layer (digital PDFs)

For digital PDFs, pypdf — the maintained successor to PyPDF2 — reads the text layer directly:

pdf_to_text.py
from pathlib import Path from pypdf import PdfReader def pdf_to_text(path: str) -> str: reader = PdfReader(path) pages = [page.extract_text() or '' for page in reader.pages] return '\n\n'.join(pages) text = pdf_to_text('document.pdf') Path('document.txt').write_text(text, encoding='utf-8') print(f'Extracted {len(text)} characters')

The or '' guard matters: extract_text() can return None for unusual pages, and one of those shouldn't crash the join. If the output scrambles multi-column layouts, switch to pdfplumber's layout mode, which spaces text according to its position on the page:

layout.py
import pdfplumber with pdfplumber.open('document.pdf') as pdf: for page in pdf.pages: print(page.extract_text(layout=True))

Step 3: OCR scanned PDFs

A scan has no text layer, so the pipeline becomes render-then-recognize: pypdfium2 (Chrome's PDF engine) renders each page to a 300 DPI image in memory, and the Image to Text API returns the words it finds:

scanned_to_text.py
import io import os import pypdfium2 as pdfium import requests session = requests.Session() session.headers['X-Api-Key'] = os.environ.get('API_NINJAS_KEY', 'YOUR_API_KEY') def scanned_pdf_to_text(path: str) -> str: """Render each page at 300 DPI and OCR it with the Image to Text API.""" pdf = pdfium.PdfDocument(path) pages = [] for index in range(len(pdf)): image = pdf[index].render(scale=300 / 72).to_pil() buffer = io.BytesIO() image.save(buffer, format='PNG') buffer.seek(0) response = session.post( 'https://api.api-ninjas.com/v1/imagetotext', files={'image': ('page.png', buffer, 'image/png')}, timeout=30, ) response.raise_for_status() words = response.json() pages.append(' '.join(w['text'] for w in words)) return '\n\n'.join(pages) print(scanned_pdf_to_text('scanned.pdf'))

Each page request comes back as a word list with pixel coordinates:

api.api-ninjas.com
POST/v1/imagetotext
200 OKapplication/json
[ { "text": "INVOICE", "bounding_box": { "x1": 412, "y1": 118, "x2": 604, "y2": 158 } }, { "text": "No.", "bounding_box": { "x1": 412, "y1": 176, "x2": 452, "y2": 200 } }, { "text": "2041", "bounding_box": { "x1": 458, "y1": 176, "x2": 512, "y2": 200 } } ]

300 DPI (scale=300/72) is the accuracy sweet spot. For the full scanned-PDF treatment — reading order, rotation, accuracy tuning — see How to OCR a PDF.

Step 4: One-off jobs — the command line

When it's a single digital file and you just want the text, skip Python entirely:

shell
# macOS: brew install poppler | Ubuntu: apt install poppler-utils pdftotext document.pdf document.txt # Preserve the original layout pdftotext -layout document.pdf document.txt

Where to go from here

If the part of the PDF you care about is a table, extract structured rows instead of prose — that's How to Convert PDF to CSV. The download below bundles detection, extraction, and OCR into one script that picks the right path per file.

pdf_to_text.py

The complete script from this guide: detects whether the PDF is digital or scanned and picks extraction or OCR automatically.

Frequently asked questions

How do I convert a PDF to text for free?

For digital PDFs, the open-source pypdf library or the pdftotext command line tool do it in seconds. For scanned PDFs, the Image to Text API OCRs page images and has a free tier — no credit card required.

Why does my PDF return empty text?

The pages are scanned images, so there is no text layer to extract. Render the pages to images and run them through OCR — Step 3 of this guide.

Does converting PDF to text preserve formatting?

Plain text keeps the words and mostly the reading order, but drops fonts and positioning. pdftotext -layout and pdfplumber’s layout mode approximate the visual arrangement; for tables specifically, extract structured rows instead — see our PDF to CSV guide.

Can I extract text from a password-protected PDF?

Yes, if you know the password: call reader.decrypt("password") after opening the file with pypdf, then extract normally.

How do I convert many PDFs at once?

Loop with pathlib — Path(folder).glob("*.pdf") — and run the same function on each file. The OCR path parallelizes well too, since every page is an independent request.