---
title: "Generating extraction schemas | Nutrient Python SDK"
canonical_url: "https://www.nutrient.io/guides/python/extraction/generate-extraction-schema/"
md_url: "https://www.nutrient.io/guides/python/extraction/generate-extraction-schema.md"
last_updated: "2026-07-10T00:00:00.000Z"
description: "Generate JSON schemas for structured data extraction using Nutrient Python SDK."
---

# Generating extraction schemas

Structured extraction starts with a JSON Schema: the schema tells the extraction model exactly which fields to fill, and an LLM's structured-output mode guarantees the result matches it. Writing that schema by hand is the slow part — you study sample documents, name every field, decide what's nullable, what repeats, what's an enum. *Schema generation* drafts it for you: describe the document type, state your requirements in plain language, attach a few example documents, and get back a schema ready for structured-output extraction.

This sample shows how to generate an extraction schema from example documents using Nutrient Python SDK. By default, generation runs an in-process local vision model, so documents don't have to leave your machine.

[Download sample](https://www.nutrient.io/downloads/samples/python/generate-extraction-schema.zip)

## How Nutrient helps

Nutrient Python SDK runs the full schema generation workflow behind a single method call. The SDK handles:

- Rendering the example documents' pages and sending them to the vision model in one request

- Drafting a schema grounded in the document type, your requirement, and the examples

- Validating and hard-clamping the draft — field budget, nesting depth, and the feature set of your target structured-output dialect

- Optionally drafting cross-field constraint rules (sums, orderings) and validating each one against the schema

- Serializing the result to JSON

The result always satisfies the limits you configured, regardless of what the model drafted — the same call with the same settings yields a schema your extraction target accepts.

## How generation works

Three inputs shape the schema:

- **Document type** (required) — The class of documents the schema must represent, such as "invoice" or "application form". It grounds the schema's vocabulary.

- **Requirement** (optional) — A free-form description of what the schema must capture: fields of interest, granularity, naming preferences — anything you'd tell a colleague.

- **Example documents** (optional, up to five) — Opened documents whose pages are rendered and shown to the model as representative samples. The schema is designed so *other* documents of the same type also fit, not just the examples. At most 20 pages are sent in total across all examples; pages beyond that bound aren't rendered.

Generation requires a vision model. By default, the SDK runs a local vision model in process and downloads the required model files on first use. To use an external OpenAI-compatible server instead, select the Custom provider and configure the connection as shown in the [describe image with local AI](https://www.nutrient.io/guides/python/extraction/describe-image-with-local-ai.md) guide. Schema generation requires the vision schema generation feature in your license.

## Targeting a structured-output dialect

LLM providers each accept a different subset of JSON Schema in their structured-output modes — one rejects `oneOf`, another rejects `maxItems`, a third silently ignores keywords it doesn't know. The `compatibility` setting picks the target dialect, and the SDK guarantees the generated schema stays inside it:

- **`PORTABLE`** (default) — The intersection accepted by the major structured-output modes and local grammar-constrained inference engines. The safest choice when you don't know where the schema will run.

- **`OPENAI`**, **`ANTHROPIC`**, **`GEMINI`** — Unlock the keywords that specific provider documents as supported, while enforcing its documented rejections.

- **`NONE`** — No dialect filter; any standard JSON Schema feature the other knobs enable.

Every generated schema also satisfies the strict-mode invariants shared by all providers: an object root, every property listed in `required`, `additionalProperties: false` on every object, and optionality expressed as a null type union rather than a missing requirement.

## Bounding the schema

Generated schemas stay within the budgets you set — properties beyond `max_fields` are truncated and structures deeper than `max_nesting_depth` are flattened, so a verbose model can't produce an unusable schema. Feature knobs (`allow_enums`, `allow_nullable`, `allow_string_formats`, and others) control which JSON Schema features the draft may use.

## Cross-field constraints

A schema describes each field in isolation; it can't say "the subtotal equals the sum of the line item amounts." Setting `include_constraints = True` asks the model to also encode such relationships as [JsonLogic](https://jsonlogic.com) constraint rules — inert JSON expressions your validation layer can evaluate after extraction. Every rule is checked against the generated schema: a rule referencing a field that doesn't exist is dropped, so the constraints always match the schema they ship with.

## Complete implementation

Import the classes used in the sample:

```python

from nutrient_sdk import Document, DocumentSettings, SchemaGenerationRequest, Vision, NutrientException

```

## Building the request

Open the example documents in [context managers](https://docs.python.org/3/reference/datamodel.html#context-managers) so resources are cleaned up after processing, then describe what the schema must represent:

```python

def main():
    try:
        with Document.open("input_forms.pdf") as first_example, \
             Document.open("input_forms_detection.pdf") as second_example:
            request = SchemaGenerationRequest()
            request.document_type = "application form"
            request.requirement = (
                "Capture the applicant's identity (full name, date of birth, contact details), "
                "every checkbox group as an enumerated field, and the signature and date block. "
                "Repeating rows belong in arrays of objects."
            )
            request.add_example_document(first_example)
            request.add_example_document(second_example)

```

## Configuring the schema shape

Create standalone document settings carrying the schema knobs — bound the field budget and opt in to cross-field constraints:

```python

            settings = DocumentSettings()
            schema_generation = settings.schema_generation_settings
            schema_generation.max_fields = 40
            schema_generation.include_constraints = True

```

## Generating the schema

Call the static `Vision.generate_schema()` with the request and settings:

```python

            result_json = Vision.generate_schema(request, settings)

            with open("output.json", "w") as f:
                f.write(result_json)
    except NutrientException as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    main()

```

## Understanding the output

`generate_schema()` returns a JSON envelope with two members — the shape is the same whether or not constraints are enabled:

- **`schema`** — The generated JSON Schema, ready to pass to an LLM structured-output mode. Every property carries a `description` telling the extractor what to put there.

- **`constraints`** — Cross-field rules (empty unless `include_constraints` is on). Each carries a short `id`, a one-sentence `description`, and a `rule` — a plain, standard JsonLogic expression that evaluates to `true` when the extracted document is internally consistent, for example, `{"==": [{"var": "total"}, {"+": [{"var": "subtotal"}, {"var": "tax_amount"}]}]}`.

Pass `schema` to your extraction call, and after extraction, evaluate each constraint rule against the extracted data with any JsonLogic implementation — a `false` result flags the document for review. Comparison policy (such as tolerating sub-cent rounding differences in numeric checks) is yours to apply in that evaluation step.

## Error handling

Vision API raises `VisionException` (a `NutrientException`) when generation fails. Common failure scenarios include a missing document type, more than five example documents, an unreachable vision model endpoint, or a model response that isn't valid JSON after a retry. In production code, catch `NutrientException`, return a clear error message, and log failure details for debugging.

## Conclusion

The workflow for generating an extraction schema is:

1. Open up to five representative example documents using context managers for automatic resource cleanup.

2. Build a `SchemaGenerationRequest` with the document type (required), a natural-language requirement, and the examples.

3. Configure the schema shape — field budget, nesting, target dialect, constraints — through `schema_generation_settings`.

4. Call `Vision.generate_schema()` to draft, validate, and clamp the schema.

5. Write the envelope to a file; feed `schema` to your structured-output extraction and `constraints` to your validation layer.

6. Handle `NutrientException` for robust error recovery.

For configuring the vision model connection, refer to the [describe image with local AI](https://www.nutrient.io/guides/python/extraction/describe-image-with-local-ai.md) guide. For related extraction workflows, refer to the [Python SDK guides](https://www.nutrient.io/guides/python.md).

Download [this ready-to-use sample package](https://www.nutrient.io/downloads/samples/python/generate-extraction-schema.zip) to explore schema generation.
---

## Related pages

- [Nutrient Python SDK extraction guides](/guides/python/extraction.md)
- [Applying OCR to a PDF page](/guides/python/extraction/apply-ocr-to-pdf-page.md)
- [Applying OCR to a PDF document](/guides/python/extraction/apply-ocr-to-pdf.md)
- [Classifying documents](/guides/python/extraction/classify-document.md)
- [Generating image descriptions using Claude](/guides/python/extraction/describe-image-with-claude.md)
- [Generating image descriptions using local AI](/guides/python/extraction/describe-image-with-local-ai.md)
- [Generating image descriptions using OpenAI](/guides/python/extraction/describe-image-with-openai.md)
- [Detecting document language](/guides/python/extraction/detect-document-language.md)
- [Extracting data from images using ICR](/guides/python/extraction/extract-data-from-image-icr.md)
- [Extracting data from images using OCR](/guides/python/extraction/extract-data-from-image-ocr.md)
- [Extracting data from images using vision language models](/guides/python/extraction/extract-data-from-image-vlm.md)
- [Extracting data from specific PDF pages](/guides/python/extraction/extract-data-from-specific-pages.md)
- [Extracting form fields from images](/guides/python/extraction/extract-form-fields-from-image.md)
- [Extracting structured data from documents](/guides/python/extraction/extract-structured-data.md)
- [Extracting structured JSON data from PDF documents](/guides/python/extraction/json-data-extraction.md)
- [Labeling form fields with a vision language model](/guides/python/extraction/label-form-fields-with-vlm.md)
- [Opening password-protected PDFs](/guides/python/extraction/open-password-protected-pdf.md)
- [Extracting text from PDF documents](/guides/python/extraction/pdf-to-text.md)
- [Reading barcodes with vision extraction](/guides/python/extraction/read-barcodes-with-vision.md)
- [Extracting text from multilingual images](/guides/python/extraction/read-text-from-image-multi-language.md)
- [Extracting text from images](/guides/python/extraction/read-text-from-image.md)
- [Later, in another process — no document needed:](/guides/python/extraction/search-document-text.md)
- [Speeding up first ICR operation by predownloading models](/guides/python/extraction/speed-up-first-icr-by-downloading-requirements.md)
- [Split documents](/guides/python/extraction/split-document.md)

