A production-ready PDF data extraction API built with FastAPI.
Upload a PDF get back text, tables, metadata, or structured invoice data. Handles edge cases cleanly so your application doesn't have to.
Most PDF libraries give you raw output and leave error handling to you. SmartPDF wraps extraction in a clean REST API with consistent error responses, scanned PDF detection, and automatic file cleanup ready to integrate into any backend pipeline.
- Extract text from digital PDFs
- Extract tables as
.xlsx(lattice → stream fallback viacamelot) - Extract PDF metadata title, author, pages, creator, creation date
- Extract structured invoice data hybrid coordinate + LLM-based extraction
- OCR support for scanned PDFs Gemini Vision extracts text from image-based PDFs automatically
- Password-protected PDF detection returns
400with descriptive message - Automatic cleanup of uploaded files after processing
- FastAPI
pdfplumbertext, metadata + coordinate-based extractioncamelottable extractionpandas+openpyxlExcel outputgoogle-genaiGemini 2.0 Flash LLM fallback for invoice extractionpython-dotenvenvironment variable management
Invoice extraction uses a three-layer hybrid approach:
- Coordinate-based extraction uses pdfplumber's word-level x/y positions to split content into left and right columns, handling two-column invoice layouts accurately
- LLM fallback if more than 50% of fields come back null, Gemini 2.0 Flash is called automatically to extract data from any invoice format
- Plain text fallback if coordinate extraction is unavailable, regex-based parsing is used as a last resort
The response includes an _extraction_method field indicating which path was taken coordinate, llm_fallback, or coordinate_partial.
app/
main.py # FastAPI app + route registration
routes/
text.py # POST /extract/text
tables.py # POST /extract/tables
metadata.py # POST /extract/metadata
invoice.py # POST /extract/invoice
services/
text_service.py
table_service.py
metadata_service.py
invoice_service.py # Hybrid coordinate + LLM extraction
utils/
pdf_utils.py
file_utils.py
config/
config.py # Environment variable loading
GET /health
{ "status": "ok", "message": "SmartPDF is running" }| Scenario | Status | Response |
|---|---|---|
| Digital PDF | 200 |
{ "text": "..." } |
| Scanned PDF | 200 |
{ "type": "scanned", "_extraction_method": "ocr_gemini", "text": "..." } |
| Password-protected | 400 |
{ "detail": "PDF is password protected" } |
| Invalid file | 422 |
Validation error |
| Scenario | Status | Response |
|---|---|---|
| Tables found | 200 |
Returns extracted_tables.xlsx file |
| No tables found | 200 |
{ "message": "No tables found in PDF" } |
| Password-protected | 400 |
{ "detail": "PDF is password protected" } |
{
"filename": "sample.pdf",
"pages": 3,
"title": "...",
"author": "...",
"creator": "...",
"created": "..."
}| Scenario | Status | Response |
|---|---|---|
| Digital PDF | 200 |
Structured invoice JSON |
| Scanned PDF | 400 |
{ "detail": "Scanned PDFs not supported for invoice extraction" } |
| Password-protected | 400 |
{ "detail": "PDF is password protected" } |
Response example:
{
"invoice_number": "INV-001",
"issue_date": "June 09, 2026",
"due_date": "June 24, 2026",
"status": "PAID",
"vendor": "Example LLC",
"bill_to": "Customer Name",
"total_amount": "47200.00",
"tax_rate": "18%",
"payment_terms": "Payment due within 15 days",
"items": [
{ "sno": 1, "name": "Python Backend Development", "unit_price": "25000.00", "qty": "1", "line_total": "25000.00" }
],
"_extraction_method": "llm_fallback"
}Get a free API key from aistudio.google.com no credit card required.
cp .env.example .env
# Add your Gemini API key in .envpython -m venv pdfenv
pdfenv\Scripts\activate # Windows
source pdfenv/bin/activate # Mac/Linuxpip install -r requirements.txt
camelotmay require Ghostscript. Install from ghostscript.com if table extraction fails.
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000- Swagger UI:
http://localhost:8000/docs - OpenAPI spec:
http://localhost:8000/openapi.json
# Extract text
curl -X POST "http://localhost:8000/extract/text" \
-F "file=@./sample.pdf"
# Extract tables (saves Excel file)
curl -X POST "http://localhost:8000/extract/tables" \
-F "file=@./table.pdf" \
--output extracted_tables.xlsx
# Extract metadata
curl -X POST "http://localhost:8000/extract/metadata" \
-F "file=@./sample.pdf"
# Extract invoice data
curl -X POST "http://localhost:8000/extract/invoice" \
-F "file=@./invoice.pdf"- OCR support for scanned PDFs (Gemini Vision via
pdf2image) - Batch processing multiple PDFs in one request
- Rate limiting per-IP request throttling
- Structured logging per-request audit trail
- Text search keyword lookup within PDF content
- Unit + integration tests (
pytest)
- Invoice extraction does not support scanned PDFs — use
/extract/textfor OCR on scanned files - OCR accuracy depends on scan quality and DPI — low-resolution scans may yield incomplete text
POPPLER_PATHmust be set in.envon Windows for OCR to work- LLM fallback requires a valid
GEMINI_API_KEYin.envwithout it, coordinate extraction result is returned as-is
MIT