Azure Document Intelligence
Azure Document Intelligence (formerly Form Recognizer) is an AI service that reads and extracts structured data from documents — invoices, receipts, ID cards, contracts, tables — using pre-trained models or custom trained models. It understands the layout, structure, and meaning of documents, not just the raw text.
Document Intelligence terms — defined before code
You have already learned about REST APIs, endpoints, API keys, and managed identity in Part 1. You have learned about async API patterns (POST then GET) in the context of other services. Document Intelligence uses that exact same async pattern. Every term below is brand new for this topic.
An Azure AI service that extracts structured data from documents. It understands document layout (where text is on the page), structure (tables, key-value pairs, checkboxes), and semantics (what type of field this is — total amount, vendor name, date). Formerly called Form Recognizer. The name changed in 2023 — the exam uses both.
A ready-to-use model that Microsoft has already trained on millions of real-world documents of a specific type — invoices, receipts, ID documents, tax forms, etc. You do not need to label your own documents or train anything. You just call the API with your document and get back structured fields immediately. Best for standard document types.
A model you train yourself using your own documents — for document types that prebuilt models don't cover (e.g. your company's unique form layout). You label at least 5 sample documents to "teach" the model what each field means. Two kinds: custom template (fixed layout, like a printed form) and custom neural (varying layouts, like different invoice formats from different vendors).
A model built by combining multiple custom models into one. The service automatically routes each document to the most appropriate component model based on the document's layout. Used when you handle many different form layouts — e.g. invoices from 10 different vendors, each with a custom model — and want a single API call to handle all of them.
A prebuilt model that extracts the raw structural layout of any document — text, tables, checkboxes, paragraphs and their positions — without assuming what kind of document it is. Use this when you need structure (table rows/columns) but don't need field-specific extraction (vendor name, total amount). The foundation of all other models.
The simplest prebuilt model — extracted printed and handwritten text only. No structure, no tables, no key-value pairs. Equivalent to the OCR Read API in Azure Vision. Used when you only need raw text extraction from an image or PDF — not document structure understanding.
The most common extraction output format. Every form has labels (keys) and their values — "Invoice Number: INV-2024-001". Document Intelligence identifies these pairs automatically. Output: {"key": "Invoice Number", "value": "INV-2024-001", "confidence": 0.99}. This is exactly what makes form processing automation possible.
The x/y coordinates (as a polygon) of where each extracted piece of text appears on the document page. Format: {polygon: [x1,y1, x2,y2, x3,y3, x4,y4]}. Useful for visual highlighting — you can draw boxes around extracted fields on a rendered document image.
A number between 0 and 1 expressing how certain the model is about each extracted value. A score of 0.98 means the model is 98% confident. Low-confidence extractions (below ~0.8) are candidates for human review. Your application should check confidence scores before using extracted values in automated workflows.
A custom model for documents with a fixed layout — the same field always appears in the same position on every document. Example: a printed government tax form where Box 1 is always in the top-left corner. Faster to train, requires only 5 sample documents. Works through positional pattern matching.
A custom model for documents with varying layouts — the same information might appear in different positions on different versions of the document. Example: invoices from different suppliers, each formatted differently. Requires more training documents (5+ minimum, but more improves accuracy). Uses deep learning — understands semantic meaning, not just position.
The API operation to submit a document for processing. It is asynchronous — you POST the document and get back an operation ID, then poll a GET endpoint until the operation completes (status: "succeeded"). This is the same POST-then-poll pattern as the OCR Read API and Azure AI batch operations. Processing can take seconds to minutes depending on document length.
A web-based graphical interface for working with Document Intelligence — accessed via documentintelligence.ai.azure.com. Used to: test prebuilt models on sample documents, label training documents for custom models, train and evaluate custom models, review extraction results interactively. You do not need to write code to label documents — the Studio handles this.
The problem it solves
Imagine you run a company that receives 50,000 invoices per month from 200 different suppliers.
Each invoice has different layouts, fonts, and field positions — but they all contain the same information:
vendor name, invoice number, line items, total amount, due date.
Traditionally, a team of data entry clerks would open each invoice and manually type these values into
your ERP system. This is slow, expensive, and error-prone.
Document Intelligence is that expert digital accountant — it reads any invoice, regardless of
layout, understands what each field means, and returns structured JSON data that your system can process
automatically. No human data entry required.
Where Document Intelligence fits in the Microsoft ecosystem
| Service | What it does | When to use it |
|---|---|---|
| Document Intelligence | Extracts structured fields and tables from documents — understands document type (invoice, receipt, ID) | When you need to understand what a document means and extract specific named fields |
| Azure AI Search (Topic 1) | Makes document content searchable — indexes text so users can search across thousands of documents | When users need to search for documents by content |
| Content Understanding (Topic 3) | Newer service — extracts and classifies content across documents, images, videos, and audio in a unified pipeline | Multi-modal content pipelines, summarisation, classification at scale |
| Vision OCR (Part 6) | Extracts raw text from images — no semantic understanding of what the text means | When you only need the raw text, not the document structure or field meaning |
Document Intelligence was called Form Recognizer until 2023. The exam may use either name — they refer to the same service. The Python package name changed too:
Old: azure-ai-formrecognizer → New: azure-ai-documentintelligence.
If the exam shows the old package name in a question, it is still Document Intelligence.
Three types of models — which to choose
Prebuilt Models — ready to use with no training
Extracts: VendorName, VendorAddress, InvoiceId, InvoiceDate, DueDate, SubTotal, TotalTax, InvoiceTotal, line items (Description, Quantity, UnitPrice, Amount). Works across many invoice layouts without training.
Extracts: MerchantName, MerchantAddress, TransactionDate, TransactionTime, Items (Name, Price, Quantity), Subtotal, Tax, Tip, Total. Optimised for point-of-sale receipts.
Extracts: FirstName, LastName, DocumentNumber, DateOfBirth, DateOfExpiration, Sex, Address, Country, Region from passports, driver's licenses, and national ID cards. Limited Access gated — requires Microsoft approval for production use.
Extracts: ContactNames (FirstName, LastName), CompanyName, JobTitles, Emails, Websites, Addresses, PhoneNumbers from business cards in various layouts.
Extracts all standard W-2 fields: Employee, Employer, Social Security wages, Federal income tax withheld, State tax information. US-specific.
Extracts: MemberName, MemberId, PlanName, Deductible, CopayInformation, GroupNumber, Payer details. US health insurance cards.
Extracts key-value pairs, tables, and entities from any general document — without assuming the document type. Use when you need structured extraction but the document type isn't covered by a specific prebuilt model.
Layout: structure + tables + all text from any document. Read: raw printed and handwritten text only. These are the foundational models — all other models build on Layout internally.
Custom Models — train on your own documents
When to use: Forms where fields always appear in the same position — printed government tax forms, standardised registration forms, fixed-format lab reports.
Minimum samples: 5 labeled documents (same layout).
How it works: Learns field positions. If "Total Amount" is always at the bottom-right of page 1, it learns that position pattern.
When to use: Documents where the same information appears in different positions — invoices from different suppliers, contracts from different law firms, medical reports from different hospitals.
Minimum samples: 5 labeled documents, but 50+ recommended for good accuracy across layout variations.
How it works: Uses deep learning to understand the semantic meaning of text context, not just position. Knows "Total Amount" because of surrounding words like "Due", "Total", "$", not its position.
Composed Models — one API, many layouts
You have 5 suppliers, each with their own invoice layout. You train 5 custom models (one per layout).
You then compose them into a single composed model. When a document arrives, the composed model
automatically classifies which component model to use, then returns the extracted fields from
the winning model — including a docType field telling you which component model was selected.
Exam trigger phrase: "documents from multiple formats / multiple suppliers / different layouts that need to go through a single endpoint."
Scenario says "same form, same layout" → Custom Template model
Scenario says "different suppliers / different layouts / variable format" → Custom Neural model
Scenario says "multiple different forms that all need one API" → Composed model (combining multiple custom models)
Scenario says "standard invoice / receipt / ID card" → Prebuilt model (no training needed)
How to train, test, and deploy a custom model
Gather at least 5 sample documents of the same type (minimum for a template model). More is better — 50+ for a neural model across varied layouts. Upload them to Azure Blob Storage in a container. Documents can be PDF, JPEG, PNG, BMP, TIFF formats. Ensure they are representative of real documents — different samples, not 5 copies of the same file.
Open Document Intelligence Studio at documentintelligence.ai.azure.com. Create a new project, connect your Blob Storage container, and open each document. For each document, draw bounding boxes around each field and assign a label name (e.g. "InvoiceNumber", "TotalAmount", "VendorName"). The studio shows the document and lets you annotate each page visually — no code needed.
Click "Train" in Document Intelligence Studio. Select the model type (Template or Neural). Give the model a name. Training takes from a few minutes (template) to longer (neural with many documents). The Studio shows training progress and completion status.
After training completes, the Studio shows evaluation metrics: estimated accuracy per field and overall. Test the model by uploading a new document (not used in training) and reviewing the extracted fields and confidence scores. If accuracy is insufficient, add more labeled training samples for the fields with low accuracy.
The trained model is now accessible via the REST API or Python SDK using your model ID. No separate "publish" step like some other services — once training completes, the model is immediately available. Pass the model_id to the begin_analyze_document call. The same async POST-then-poll pattern applies.
How analyze_document works — POST then poll
Document Intelligence uses an asynchronous API for all analysis operations. Here is why and how it works:
When you send a document to a print queue, you don't stand frozen waiting for it to print — you submit the job
and then check if it's done periodically. Document analysis (especially multi-page PDFs) takes time, so the API
works the same way:
Step 1 — Submit: POST the document → you get back an operation ID (like a ticket number).
Step 2 — Poll: Use the operation ID to GET the status repeatedly until it says "succeeded", then read the results.
The Python SDK hides this polling loop for you inside begin_analyze_document() — it returns a
"poller" object. Calling .result() on the poller waits until the operation completes and returns the results.
Analyze a receipt with a prebuilt model, then a custom model
Install: pip install azure-ai-documentintelligence azure-core
import os
from azure.ai.documentintelligence import DocumentIntelligenceClient
from azure.ai.documentintelligence.models import AnalyzeDocumentRequest
from azure.core.credentials import AzureKeyCredential
# ── Connection details ─────────────────────────────────────────────────
# These come from your Document Intelligence resource in Azure portal
# Keys and Endpoint → copy the endpoint URL and one of the two keys
endpoint = os.environ.get("DOCUMENTINTELLIGENCE_ENDPOINT")
key = os.environ.get("DOCUMENTINTELLIGENCE_KEY")
# Create a credential object — wraps the key in a standard credential type
credential = AzureKeyCredential(key)
# Create the client — this object provides all data extraction methods
# The endpoint format is: https://<your-resource-name>.cognitiveservices.azure.com/
client = DocumentIntelligenceClient(endpoint=endpoint, credential=credential)
# ══════════════════════════════════════════════════════════════════════
# EXAMPLE 1 — Prebuilt Receipt Model
# Use this when you have a standard point-of-sale receipt
# No training needed — Microsoft has pre-trained this model
# ══════════════════════════════════════════════════════════════════════
# URL of the document to analyse (can also use a local file — see Example 2)
receipt_url = "https://raw.githubusercontent.com/Azure-Samples/cognitive-services-REST-api-samples/master/curl/form-recognizer/contoso-receipt.png"
# begin_analyze_document() submits the document for analysis
# "prebuilt-receipt" is the model ID for the prebuilt receipt model
# Other prebuilt model IDs: "prebuilt-invoice", "prebuilt-idDocument", "prebuilt-businessCard"
# "prebuilt-layout" (structure + tables), "prebuilt-read" (text only)
# AnalyzeDocumentRequest wraps the document URL (can also use urlSource= parameter directly)
poller = client.begin_analyze_document(
"prebuilt-receipt", # Which model to use
AnalyzeDocumentRequest(url_source=receipt_url) # The document URL to analyse
)
# .result() waits for the async operation to complete and returns the result
# The Python SDK handles the POST → poll → GET loop automatically
# This may take several seconds for large documents
result = poller.result()
# The result contains a list of documents — one per recognised document in the input
# For a single receipt image, there will be one document
for receipt in result.documents:
# doc_type: which prebuilt sub-type was detected (e.g. "receipt.retailMeal")
print(f"Document Type: {receipt.doc_type}")
print(f"Confidence: {receipt.confidence:.2f}")
# receipt.fields is a dictionary of field name → AnalyzedDocumentField objects
# Each field has: .value (the extracted value), .confidence (0.0 to 1.0)
# .get() returns None if the field wasn't found in this document
merchant = receipt.fields.get("MerchantName")
total = receipt.fields.get("Total")
date = receipt.fields.get("TransactionDate")
# Always check if the field was found before accessing its value
if merchant:
# .value gives you the extracted value in a Python-friendly type
# .confidence tells you how certain the model is (0.0 = uncertain, 1.0 = certain)
print(f"Merchant: {merchant.value} (confidence: {merchant.confidence:.2f})")
if total:
# For currency fields, .value is a CurrencyValue object with .amount and .currency_symbol
print(f"Total: {total.value.amount} {total.value.currency_symbol} (confidence: {total.confidence:.2f})")
if date:
# For date fields, .value is a Python datetime.date object
print(f"Date: {date.value}")
# Receipts also contain line items — stored as a list in the Items field
items = receipt.fields.get("Items")
if items:
print("\nLine Items:")
# items.value is a list of ValueArray objects, one per line item
for item in items.value:
# Each item is itself a dictionary of fields (Description, Price, Quantity)
desc = item.value.get("Description")
price = item.value.get("Price")
if desc and price:
print(f" {desc.value}: {price.value.amount}")
# ══════════════════════════════════════════════════════════════════════
# EXAMPLE 2 — Analyse a LOCAL FILE using the prebuilt Invoice model
# ══════════════════════════════════════════════════════════════════════
# Open a local PDF file in binary mode ("rb" = read binary)
# Binary mode is needed because we are sending raw bytes, not text
with open("invoice.pdf", "rb") as f:
# f.read() reads the entire file as a bytes object
document_bytes = f.read()
# When passing a local file, use bytes_source= instead of url_source=
poller = client.begin_analyze_document(
"prebuilt-invoice", # Model: prebuilt invoice
AnalyzeDocumentRequest(bytes_source=document_bytes) # Local file as bytes
)
result = poller.result()
for invoice in result.documents:
vendor_name = invoice.fields.get("VendorName")
invoice_id = invoice.fields.get("InvoiceId")
invoice_total = invoice.fields.get("InvoiceTotal")
due_date = invoice.fields.get("DueDate")
print("\n--- Invoice Extraction ---")
if vendor_name: print(f"Vendor: {vendor_name.value}")
if invoice_id: print(f"Invoice ID: {invoice_id.value}")
if invoice_total: print(f"Total Amount: {invoice_total.value.amount}")
if due_date: print(f"Due: {due_date.value}")
# ══════════════════════════════════════════════════════════════════════
# EXAMPLE 3 — Custom Model
# Use the same client and same begin_analyze_document() call
# The only difference: pass YOUR model ID instead of a prebuilt model ID
# ══════════════════════════════════════════════════════════════════════
# Your custom model ID — found in Document Intelligence Studio after training
# Format: a GUID like "abc12345-1234-5678-abcd-123456789abc"
my_custom_model_id = "your-custom-model-id-here"
with open("my_custom_form.pdf", "rb") as f:
document_bytes = f.read()
# Same API call — just replace the model ID with your trained custom model ID
poller = client.begin_analyze_document(
my_custom_model_id,
AnalyzeDocumentRequest(bytes_source=document_bytes)
)
result = poller.result()
for doc in result.documents:
# doc_type: "your-model-id:Type" — the custom model type name you defined
print(f"Doc type: {doc.doc_type}, Confidence: {doc.confidence:.2f}")
# doc.fields is a dictionary of the LABEL NAMES you used when labeling in Studio
for field_name, field_value in doc.fields.items():
if field_value.value:
print(f" {field_name}: {field_value.value} (confidence: {field_value.confidence:.2f})")
Python — Extracting tables and bounding box positions
Documents often contain tables — rows and columns of structured data. Document Intelligence extracts them with full position information.
# Use the Layout model to extract tables from any document
# "prebuilt-layout" extracts text + tables + structure without assuming a document type
with open("contract_with_table.pdf", "rb") as f:
document_bytes = f.read()
poller = client.begin_analyze_document(
"prebuilt-layout",
AnalyzeDocumentRequest(bytes_source=document_bytes)
)
result = poller.result()
# result.tables is a list of all detected tables in the document
for i, table in enumerate(result.tables):
# row_count, column_count: dimensions of the detected table
print(f"\nTable {i+1}: {table.row_count} rows × {table.column_count} cols")
# table.cells is a flat list of all cells in the table
# Each cell has: row_index, column_index, content, bounding_regions
for cell in table.cells:
# row_index and column_index tell you where in the table this cell sits
# kind: "columnHeader" for header row cells, "content" for data cells
label = "[HEADER]" if cell.kind == "columnHeader" else ""
print(f" Row {cell.row_index}, Col {cell.column_index} {label}: {cell.content}")
# Bounding box — where on the page each piece of content appears
# result.pages contains all the pages with their words and lines
for page in result.pages:
print(f"\nPage {page.page_number}: {page.width}w × {page.height}h ({page.unit})")
# page.words is a flat list of every recognised word on the page
for word in page.words:
# word.polygon: the [x1,y1, x2,y2, x3,y3, x4,y4] corners of the word's bounding box
# word.confidence: how confident the OCR was about this word (0.0 to 1.0)
# word.content: the text of the word
print(f" Word: '{word.content}' conf={word.confidence:.2f}")
# The polygon lets you draw a box around the word on a rendered document image
Document Intelligence — what the exam tests
1. Minimum 5 labeled documents to train a custom model — The exam uses 5 as a threshold question. "How many labeled documents are required to train a custom model?" → 5. More improves accuracy but 5 is the minimum.
2. Custom Template vs Custom Neural — Template = same field always in same position (fixed layout). Neural = fields in varying positions across documents (variable layout). Wrong model type = poor accuracy. This is a common scenario question.
3. Composed model = multiple custom models + automatic routing — "A company receives invoices from 8 different suppliers, each with a different format, via one API endpoint" → Composed model. Each supplier gets its own custom model, then composed into one.
4. Old name = Form Recognizer, new name = Document Intelligence — Both names appear in exam questions. The Python package changed from azure-ai-formrecognizer to azure-ai-documentintelligence. Don't let the old name confuse you.
5. Analyze operation is asynchronous — Document Intelligence never returns results immediately. It always follows the POST → operation ID → poll GET → status "succeeded" → read results pattern. The Python SDK's .result() handles the polling for you.
6. ID Document model requires Limited Access approval — Just like Face API identification. If a scenario involves extracting data from passports or driver's licenses you need Microsoft's gated approval — can't just enable and use freely.
Test your understanding
Which Document Intelligence model type is most appropriate, and approximately how many labeled training samples are required to start?
C is correct. The key phrase is "fixed layout" — fields always appear in the same position. This perfectly matches the Custom Template model, which learns positional patterns rather than semantic meaning. The minimum training requirement is 5 labeled documents. Custom Neural (B) is for varying layouts and benefits from more samples but 50 isn't a hard requirement. Prebuilt Invoice (A) is for standard invoices — not mortgage applications. Layout (D) extracts raw structure but doesn't extract named semantic fields like "loan amount".