Use this skill for intelligent document processing and content extraction using LandingAI's Agentic Document Extraction (ADE). Trigger when users need to (1) Parse documents (PDFs, images, spreadsheets, presentations) into structured Markdown with layout understanding, (2) Extract specific structured data from documents using schemas (invoice fields, form data, table data, etc.), (3) Classify and separate multi-document batches by type (invoices vs receipts, statements vs forms, etc.), (4) Process large documents asynchronously (up to 1GB/1000 pages), (5) Get visual grounding (bounding boxes, page numbers) for extracted content — use when users mention bounding boxes, word locations, grounding, highlighting extracted content, or showing where data appears in a document. Use this skill when the task involves understanding document content for a set of documents. In particular this skill can help you write code that run on sets of documents. This will increase speed, and reduce the cost of loading the documents
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Use this skill for intelligent document processing and content extraction using LandingAI's Agentic Document Extraction (ADE). Trigger when users need to (1) Parse documents (PDFs, images, spreadsheets, presentations) into structured Markdown with layout understanding, (2) Extract specific structured data from documents using schemas (invoice fields, form data, table data, etc.), (3) Classify and separate multi-document batches by type (invoices vs receipts, statements vs forms, etc.), (4) Process large documents asynchronously (up to 1GB/1000 pages), (5) Get visual grounding (bounding boxes, page numbers) for extracted content — use when users mention bounding boxes, word locations, grounding, highlighting extracted content, or showing where data appears in a document. Use this skill when the task involves understanding document content for a set of documents. In particular this skill can help you write code that run on sets of documents. This will increase speed, and reduce the cost of loading the documents on the Agent context window because you can use a single script to extract the information needed.
Document Extraction (ADE)
Overview
LandingAI's Agentic Document Extraction (ADE) is a document processing SaaS that parses, extracts, and classifies documents without requiring templates or training. It provides three main capabilities:
Parse: Convert documents into structured Markdown with hierarchical JSON representation
Extract: Pull specific structured data using JSON schemas or Pydantic models
Split: Classify and separate multi-document batches by type
Key Benefits:
No ML training or templates required
Layout-agnostic parsing (works with any document structure)
Multiple models optimized for different document types
Quick Start
1. Installation
Never install packages globally without user approval. Always check for a local Python environment first.
1. .venv/bin/python — uv-managed (this project)
2. venv/bin/python — standard Python venv
3. uv run python — if pyproject.toml exists
4. poetry run python — if poetry.lock exists
5. python3 — system fallback; warn the user
Use the local environment to install: landingai-ade, python-dotenv
2. API Key Setup
The user may have already setup a .env file in the same directory as the document-extraction skill with the API key. You MUST check this path first (ls -la .*/skills/document-extraction/.env). Also try checking on the same directory as this SKILL.md file.
If not, provide instructions to create one. The script below will search for .env in common locations and load it.
.venv/bin/python - << 'EOF'
import os
from pathlib import Path
from dotenv import load_dotenv
# Load API key: prefer existing env var, then .env file lookupif os.environ.get("VISION_AGENT_API_KEY"):
print("API key found in existing environment variable")
else:
def _find_env():
for d in [Path.cwd().resolve(), *Path.cwd().resolve().parents]:
for candidate in [
# ADD the directory where the document-extraction skill is located
d / '.env',
d / 'document-extraction/.env',
d / 'skills/document-extraction/.env',
]:
if candidate.is_file():
return candidate
return None
env = _find_env()
ifenv:
load_dotenv(env)
print(f"API key loaded from: {env}")
else:
print("Warning: VISION_AGENT_API_KEY not set and no .env found")
EOF
Recommendation: Use dpt-2-latest unless you have simple documents where cost/speed is critical.
Version Pinning: For production, use dated versions (e.g., dpt-2-20260302) for reproducibility.
Parse Large Files (Async)
For files up to 1 GB or 6,000 pages, use Parse Jobs:
import time
from dotenv import load_dotenv
load_dotenv()
from landingai_ade import LandingAIADE
from pathlib import Path
client = LandingAIADE()
# Step 1: Create parse job
job = client.parse_jobs.create(
document=Path("large_document.pdf"),
model="dpt-2-latest"
)
job_id = job.job_id
print(f"Job {job_id} created")
# Step 2: Poll for completionwhileTrue:
response = client.parse_jobs.get(job_id)
if response.status == "completed":
print(f"Job {job_id} completed")
breakprint(f"Progress: {response.progress * 100:.0f}%")
time.sleep(5)
# Step 3: Access results# Results are in response.data (or response.output_url for large results)if response.data:
print(f"Chunks: {len(response.data.chunks)}")
withopen("output.md", "w", encoding="utf-8") as f:
f.write(response.data.markdown)
elif response.output_url:
# Results > 1MB are returned as a presigned URLprint(f"Download results from: {response.output_url}")
Job Status Response Fields:
job_id, status (pending, processing, completed, failed, cancelled), progress (0-1)
data: The ParseResponse (or SpreadsheetParseResponse) when complete and result < 1MB
output_url: Presigned S3 URL when result > 1MB or when output_save_url was used. Expires after 1 hour; a new URL is generated on each GET.
metadata: Same as sync parse (filename, page_count, duration_ms, etc.)
failure_reason: Error message if job failed
Zero Data Retention (ZDR)
If ZDR is enabled for your organization, you must provide an output_save_url where parsed results will be saved. The results will not be returned in the API response. ZDR is not enabled by default. Typically output_save_url is a presigned url with write permissions to your S3 bucket, but you can also use other storage solutions that support file uploads via HTTP PUT requests.
List all async parse jobs with optional pagination and status filtering:
# List recent jobs
jobs_response = client.parse_jobs.list(page=0, page_size=10)
for job in jobs_response.jobs:
print(f"{job.job_id}: {job.status} ({job.progress:.0%})")
# Filter by status
completed = client.parse_jobs.list(status="completed", page_size=5)
print(f"Completed jobs: {len(completed.jobs)}, more: {completed.has_more}")
Available status filters:pending, processing, completed, failed, cancelled
Understanding Parse Outputs
Parse returns a ParseResponse with:
markdown: Complete document in Markdown with HTML anchor tags
chunks: Array of extracted elements (each with unique ID, type, content, and per-chunk grounding)
grounding: Dictionary mapping element IDs to detailed location data (page, bounding box, grounding type, and table cell position). See JSON Response for structure.
splits: Array of split objects grouping chunks. Always present — contains a single "full" split by default, or per-page splits if split="page" was used. Note: Parse splits use a class field (values: "full" or "page"), which is different from the Split API's classification field.
Anchor tag prefix in chunk.markdown: Every chunk's markdown field
is prefixed with an HTML anchor tag embedding the chunk UUID:
<a id='abc123...'></a>\n\nActual content…. This is how the full document
markdown links back to individual chunks. Strip it before string matching,
display, or RAG indexing:
import re
_ANCHOR_RE = re.compile(r"<a[^>]*></a>\s*", re.IGNORECASE)
defchunk_text(ch) -> str:
"""Return clean chunk markdown without the anchor prefix."""return _ANCHOR_RE.sub("", ch.markdown or"").strip()
# Example: fingerprint match against a section of the full markdown
intro_chunks = [ch for ch in response.chunks
if chunk_text(ch)[:80] in intro_markdown]
Saving Parse Responses
The SDK provides a built-in save_to parameter on parse(), extract(), and split() that automatically saves the JSON response to a folder:
from pathlib import Path
# Parse and auto-save response JSON to output/ folder
response = client.parse(
document=Path("document.pdf"),
model="dpt-2-latest",
save_to="output/"# Creates output/document_parse_output.json
)
# Response is still returned normally for immediate useprint(response.markdown[:200])
The save_to parameter:
Creates the folder if it doesn't exist
Names the file {input_filename}_{method}_output.json (e.g., document_parse_output.json)
Works on client.parse(), client.extract(), and client.split()
Is a client-side convenience — it saves the full response locally after the API call
For manual serialization (e.g., custom filenames or selective saving), use model_dump():
import json
response_dict = response.model_dump()
withopen("parse_response.json", "w", encoding="utf-8") as f:
json.dump(response_dict, f, indent=2, ensure_ascii=False)
# Save markdown separately for extractionwithopen("document_parsed.md", "w", encoding="utf-8") as f:
f.write(response.markdown)
Important: Always use model_dump() to serialize the complete response. Do not manually construct dictionaries with selected fields, as you may miss important data like the splits array or complete grounding information.
Organizations with Zero Data Retention (ZDR) enabled can parse password-protected files by passing the password parameter. Supported formats: PDF, DOC, DOCX, ODT, PPT, PPTX, XLSX.
Note: Without ZDR the API returns HTTP 422. If the password is wrong the API
returns HTTP 422 with a decryption error. The parameter is ignored for unencrypted documents.
Structured Data Extraction
Schema Definition
Define what to extract using JSON Schema or Pydantic models.
Pydantic approach (recommended for Python):
from pydantic import BaseModel, Field
from landingai_ade.lib import pydantic_to_json_schema
classBankStatement(BaseModel):
account_holder: str = Field(description="Account holder name")
account_number: str = Field(description="Account number")
beginning_balance: float = Field(description="Beginning balance in USD")
ending_balance: float = Field(description="Ending balance in USD")
schema = pydantic_to_json_schema(BankStatement)
Top-level grounding is a dictionary keyed by element ID (UUID for chunks, {page}-{base62} for tables/cells). Each value contains box, page, type, and optionally confidence and low_confidence_spans (see Confidence Scores). Table cell entries also include a position field (see Grounding and Traceability).
Grounding Type Mapping
Grounding types use a chunk prefix to distinguish them from chunk types. The table and tableCell types are grounding-only (no corresponding chunk type):
schema_violation_error: null when extraction matches schema. Contains a detailed error message when the extracted data doesn't fully conform (HTTP 206 response). Extraction still returns partial data and consumes credits.
fallback_model_version: null normally. Contains the model version actually used when the initial extraction attempt failed with the requested version and a fallback was used.
Grounding and Traceability
Every parsed element includes precise location information in the top-level grounding dictionary:
Page references: Zero-indexed page numbers
Bounding boxes: Normalized coordinates (0-1) for position
left, top, right, bottom
Convert to pixels: multiply by image dimensions
Element IDs: UUID for chunks, {page}-{base62} for tables and table cells
Table/cell IDs use sequential base62 numbering per page: 0-1, 0-2, ..., 0-9, 0-a, ..., 0-z, 0-A, ..., 0-Z, 0-10, etc.
Numbering restarts on each page (e.g., first table on page 1 → 1-1)
Grounding types: Each entry has a type field using prefixed names (e.g., chunkText, chunkTable). See Grounding Type Mapping.
Table cell position: tableCell entries include a position object with row, col (zero-indexed), rowspan, colspan, and chunk_id (the parent table chunk UUID)
Extraction metadata: Shows which chunks/cells provided each field
Per-chunk grounding (on each chunk object) contains only box and page. The top-level grounding dictionary adds type and, for table cells, position.
Example:
# Per-chunk grounding (basic location)for chunk in response.chunks:
print(f"Chunk {chunk.id} on page {chunk.grounding.page}")
bbox = chunk.grounding.box
print(f"Location: ({bbox.left}, {bbox.top}) to ({bbox.right}, {bbox.bottom})")
# Top-level grounding (detailed, with type and position)# NOTE: grounding values are Pydantic models — use attribute access, not dict accessfor elem_id, info in response.grounding.items():
print(f"{elem_id}: type={info.type}, page={info.page}")
if info.type == "tableCell"and info.position:
print(f" Cell at row={info.position.row}, col={info.position.col}")
Important:response.grounding is a Dict[str, Grounding] — the outer container is a dict (so .items(), .get() work), but each value is a Pydantic model. Use attribute access (info.type, info.box.left) not dict access (info["type"]).
Confidence Scores {#confidence-scores}
Top-level grounding entries may include confidence information:
confidence (float | None): Overall confidence score (0.0–1.0) for the chunk's transcription
low_confidence_spans (list | None): Specific text spans with low confidence, each containing:
confidence (float): Span-level confidence score
text (str): The low-confidence text
span (list): Position markers within the chunk
# Access confidence scores from top-level groundingfor elem_id, info in response.grounding.items():
if info.confidence isnotNone:
print(f"{elem_id}: confidence={info.confidence:.2f}")
for span in info.low_confidence_spans or []:
print(f" Low confidence ({span.confidence:.2f}): "f"'{span.text}'")
Notes:
Confidence is only present in top-level grounding (not per-chunk grounding)
Not all grounding entries will have confidence (e.g., table/tableCell types may not)
Use confidence scores to flag chunks that may need human review
Best Practices
Model Selection
Use dpt-2-latest for most documents (complex layouts, logos, signatures)
Use dpt-2-mini for simple, digitally-native documents (faster, cheaper)
Pin versions in production for reproducibility (e.g., dpt-2-20260302)
Use extract-latest for extraction (automatically uses newest model)
Do NOT use dpt-1 — deprecated March 31, 2026; migrate to dpt-2
Schema Design
Be specific: Use descriptive field names (invoice_number not number)
Add descriptions: Include format requirements ("in USD", "as YYYY-MM-DD")
Keep it simple: Start with few fields, add more as needed
Limit complexity: Under 30 properties for optimal performance
Match document structure: Order fields as they appear in document
See references/use-cases.md for complete worked examples: invoice processing, form data extraction, multi-document classification, table extraction, and figure cropping with PyMuPDF.
Troubleshooting
See references/troubleshooting.md for HTTP error codes, parse failures, extraction accuracy issues, schema validation errors, and performance guidance.