Skip to content

🐍 Using datamodel-code-generator as a Module

datamodel-code-generator is a CLI tool, but it can also be used as a Python module.

🚀 How to Use

You can generate models with datamodel_code_generator.generate using parameters that match the CLI arguments.

📦 Installation

The base package is sufficient when all inputs and references are local. To load an HTTP(S) input or resolve a remote $ref, install the stable HTTP extra:

pip install 'datamodel-code-generator[http]'

The http extra uses HTTPX and is supported and not deprecated. The experimental HTTPX2 backend is available separately:

pip install 'datamodel-code-generator[httpx2]'

The experimental extra is not included in datamodel-code-generator[all]. The default HTTPBackend.AUTO policy selects stable HTTPX when its client module is installed, including when both extras are installed, and selects HTTPX2 only when that module is absent. Require HTTPX2 with --http-backend httpx2, http_backend = "httpx2" under [tool.datamodel-codegen], or the public API:

from urllib.parse import urlparse

from datamodel_code_generator import HTTPBackend, generate

result = generate(
    urlparse("https://example.com/schema.json"),
    http_backend=HTTPBackend.HTTPX2,
)

See HTTP backend selection for the complete automatic and explicit selection rules.

📝 Getting Generated Code as String

When the output parameter is omitted (or set to None), generate() returns the generated code directly as a string:

Note

GenerateConfig requires a Pydantic v2 environment.

from datamodel_code_generator import InputFileType, generate, GenerateConfig, DataModelType

json_schema: str = """{
    "type": "object",
    "properties": {
        "number": {"type": "number"},
        "street_name": {"type": "string"},
        "street_type": {"type": "string",
                        "enum": ["Street", "Avenue", "Boulevard"]
                        }
    }
}"""

config = GenerateConfig(
    input_file_type=InputFileType.JsonSchema,
    input_filename="example.json",
    output_model_type=DataModelType.PydanticV2BaseModel,
)
result = generate(json_schema, config=config)
print(result)

Loading Project Configuration

generate() uses the options passed to it. The settings the command line implies apply the same way: msgspec.Struct output turns on use_annotated unless it is passed, and use_annotated turns on field_constraints. Pass use_annotated=False to keep msgspec models without Annotated constraints, as generate() wrote them before 0.84.0.

To reuse [tool.datamodel-codegen] from pyproject.toml, explicitly load a GenerateConfig:

from datamodel_code_generator import generate, load_pyproject_config

config = load_pyproject_config()
result = generate(json_schema, config=config)

The loader searches upward from the working directory, stopping at a Git project boundary, as the CLI does. Pass path="project", a Path, or a pyproject.toml path to start elsewhere, and profile="models" to select a profile, including its extends inheritance. Without project settings, it returns the API defaults; a requested profile must exist.

Both a GenerateConfig and a dictionary of individual Python API options can override the project settings:

from datamodel_code_generator import GenerateConfig, InputFileType

options = {"input_file_type": InputFileType.JsonSchema, "output": None}
config = load_pyproject_config(overrides=options)
result = generate(json_schema, config=config)

existing_config = GenerateConfig(output=None, disable_timestamp=True)
config = load_pyproject_config(profile="models", overrides=existing_config)
result = generate(json_schema, config=config)

The precedence is API defaults, project settings, profile settings, then overrides. Only explicitly set fields of a GenerateConfig override are merged; explicit False, None, and empty lists also replace project values. Paths loaded from TOML are relative to its directory, including JSON configuration files such as aliases, unlike the command line, which reads those JSON files from the working directory. Override paths remain relative to the caller's working directory. The project directory supplies formatter settings unless settings_path is explicitly overridden. CLI-only settings such as input, check, and batch jobs are ignored: pass the schema to generate() yourself. The loader validates the merged settings and preserves the existing rule that config= cannot be combined with individual generate() options.

📝 Reading Input From a File

Pass a Path to input_ for file input. Plain strings are treated as schema text; if a string points to an existing file and parsing fails, generate() warns and recommends using Path.

from pathlib import Path

from datamodel_code_generator import InputFileType, generate

result = generate(
    input_=Path("example.json"),
    input_file_type=InputFileType.JsonSchema,
)
print(result)

📝 Multiple Module Output

When the schema generates multiple modules, generate() returns a GeneratedModules dictionary mapping module path tuples to generated code:

from datamodel_code_generator import InputFileType, generate, GenerateConfig, GeneratedModules

# Your OpenAPI specification (string schema text, Path, or dict)
openapi_spec: str = "..."  # Replace with your actual OpenAPI spec

# Schema that generates multiple modules (e.g., with $ref to other files)
config = GenerateConfig(
    input_file_type=InputFileType.OpenAPI,
)
result: str | GeneratedModules = generate(openapi_spec, config=config)

if isinstance(result, dict):
    for module_path, content in result.items():
        print(f"Module: {'/'.join(module_path)}")
        print(content)
        print("---")
else:
    print(result)

📝 Writing to Files

To write generated code to the file system, provide a Path to the output parameter in the config:

from pathlib import Path
from tempfile import TemporaryDirectory
from datamodel_code_generator import InputFileType, generate, GenerateConfig, DataModelType

json_schema: str = """{
    "type": "object",
    "properties": {
        "number": {"type": "number"},
        "street_name": {"type": "string"},
        "street_type": {"type": "string",
                        "enum": ["Street", "Avenue", "Boulevard"]
                        }
    }
}"""

with TemporaryDirectory() as temporary_directory_name:
    temporary_directory = Path(temporary_directory_name)
    output = Path(temporary_directory / "model.py")
    config = GenerateConfig(
        input_file_type=InputFileType.JsonSchema,
        input_filename="example.json",
        output=output,
        # set up the output model types
        output_model_type=DataModelType.PydanticV2BaseModel,
    )
    generate(json_schema, config=config)
    model: str = output.read_text()
print(model)

✨ Output:

# generated by datamodel-codegen:
#   filename:  example.json
#   timestamp: 2020-12-21T08:01:06+00:00

from __future__ import annotations

from enum import Enum
from typing import Optional

from pydantic import BaseModel


class StreetType(Enum):
    Street = "Street"
    Avenue = "Avenue"
    Boulevard = "Boulevard"


class Model(BaseModel):
    number: Optional[float] = None
    street_name: Optional[str] = None
    street_type: Optional[StreetType] = None


Generating a Server or Client Package

Experimental

Server and client generation is experimental; its options and generated packages may change.

generate() also generates the FastAPI server or the HTTPX2 client package of an OpenAPI document together with its models. generate_server or generate_client selects the package, and the server_* or client_* options configure it. They are the options of --generate-server, --generate-client, and the --server-* and --client-* flags under their Python names, as for model options: choices are enums such as ServerType (their string values also work), and the options that take JSON on the command line take mappings.

from pathlib import Path

from datamodel_code_generator import (
    DataModelType,
    InputFileType,
    OpenAPIScope,
    ServerHandlerMode,
    ServerType,
    generate,
)

generate(
    Path("openapi.yaml"),
    input_file_type=InputFileType.OpenAPI,
    openapi_scopes=[OpenAPIScope.Schemas, OpenAPIScope.Api],
    output_model_type=DataModelType.PydanticV2BaseModel,
    preset="standard-py312-20261106",
    output=Path("app/models.py"),
    generate_server=ServerType.FastAPI,
    server_output=Path("app/server"),
    server_package="app.server",
    server_model_package="app.models",
    server_handler_mode=ServerHandlerMode.Async,
    server_body_modes={"/paths/~1upload/post": "request"},
)

With an output, generate() writes the models and the package together and returns None; for a server, it also writes each missing module of the services package beside the package once, with stubs, as the command line does. Without one, it writes nothing but the model metadata and a remote lock update, no stubs among them, and returns GeneratedModules with every model and package file, each under the path its import path implies: server_model_package="app.models" gives ("app", "models.py"), or ("app", "models", "__init__.py") and the other modules of modular models, and server_package="app.server" gives ("app", "server", "application.py"), ("app", "server", "README.md"), and so on. These are the files a run with output and server_output in the working directory writes; server_output is not used. The run renders the files in a private directory of the working directory, which it removes afterwards, so the working directory must be writable. load_pyproject_config() takes the server and client keys of pyproject.toml too, so a config it loads with overrides={"output": None} returns the files of the configured run.

The selector requires server_package and server_model_package (or the client ones), and server_output with an output. A model setting the target needs that neither the arguments nor the preset set, such as openapi_scopes or target_python_version, takes the value the target needs, without printing anything; see Requirements. A missing option, an option the target cannot use, and a failure of the package raise datamodel_code_generator.Error with the message the command line prints. The server_* and client_* options have no effect without their selector, as their pyproject.toml keys do.


🔧 Using the Parser Directly

You can also call the parser directly for more control. Parser classes also support the config parameter similar to generate().

from datamodel_code_generator import DataModelType, PythonVersion
from datamodel_code_generator.config import JSONSchemaParserConfig
from datamodel_code_generator.model import get_data_model_types
from datamodel_code_generator.parser.jsonschema import JsonSchemaParser

json_schema: str = """{
    "type": "object",
    "properties": {
        "number": {"type": "number"},
        "street_name": {"type": "string"},
        "street_type": {"type": "string",
                        "enum": ["Street", "Avenue", "Boulevard"]
                        }
    }
}"""

data_model_types = get_data_model_types(DataModelType.PydanticV2BaseModel, target_python_version=PythonVersion.PY_311)
config = JSONSchemaParserConfig(
    data_model_type=data_model_types.data_model,
    data_model_root_type=data_model_types.root_model,
    data_model_field_type=data_model_types.field_model,
    data_type_manager_type=data_model_types.data_type_manager,
    dump_resolve_reference_action=data_model_types.dump_resolve_reference_action,
)
parser = JsonSchemaParser(json_schema, config=config)
result = parser.parse()
print(result)

Using Keyword Arguments (Backward Compatible)

from datamodel_code_generator import DataModelType, PythonVersion
from datamodel_code_generator.model import get_data_model_types
from datamodel_code_generator.parser.jsonschema import JsonSchemaParser

json_schema: str = """{
    "type": "object",
    "properties": {
        "number": {"type": "number"},
        "street_name": {"type": "string"},
        "street_type": {"type": "string",
                        "enum": ["Street", "Avenue", "Boulevard"]
                        }
    }
}"""

data_model_types = get_data_model_types(DataModelType.PydanticV2BaseModel, target_python_version=PythonVersion.PY_311)
parser = JsonSchemaParser(
    json_schema,
    data_model_type=data_model_types.data_model,
    data_model_root_type=data_model_types.root_model,
    data_model_field_type=data_model_types.field_model,
    data_type_manager_type=data_model_types.data_type_manager,
    dump_resolve_reference_action=data_model_types.dump_resolve_reference_action,
)
result = parser.parse()
print(result)

Available Parser Config Classes

Each parser type has its own config class:

Parser Config Class
JsonSchemaParser JSONSchemaParserConfig
OpenAPIParser OpenAPIParserConfig
AvroParser AvroParserConfig
GraphQLParser GraphQLParserConfig

All config classes inherit from ParserConfig and include additional parser-specific options.

✨ Output:

from __future__ import annotations

from enum import Enum
from typing import Optional

from pydantic import BaseModel


class StreetType(Enum):
    Street = "Street"
    Avenue = "Avenue"
    Boulevard = "Boulevard"


class Model(BaseModel):
    number: Optional[float] = None
    street_name: Optional[str] = None
    street_type: Optional[StreetType] = None


📋 Return Value Summary

output Parameter Single Module Multiple Modules
None (default) str GeneratedModules (dict)
Path (file) None Error
Path (directory) None None

📌 Note: When output is a file path and multiple modules would be generated, generate() raises a datamodel_code_generator.Error exception. Use a directory path instead.

With generate_server or generate_client, generate() returns None with an output and GeneratedModules with every model and package file without one (see Generating a Server or Client Package).


📖 See Also