The SDK's BaseModel extends Pydantic with lenient field handling, version-agnostic deserialization, and API-aligned serialization (camelCase keys by default) so callers can work uniformly across Pydantic v1 and v2. BaseAPIResponse wraps HTTP responses with retry counts, elapsed time, and lazy parsed-result caching, while _parse() intelligently deserializes JSON, primitives, and SSE streams—falling back to raw text when strict validation is disabled.
BaseModel in src/anthropic/_models.py extends pydantic.BaseModel and is configured with extra='allow' so that unknown API fields are stored rather than rejected.[1] The public exports from src/anthropic/_models.py are BaseModel and GenericModel, declared via __all__.[1] Pydantic v1 compatibility shims (model_dump, model_dump_json, model_fields_set) are defined on BaseModel so callers can always use the Pydantic v2 API regardless of which Pydantic version is installed.[1]
BaseModel.to_dict() defaults to use_api_names=True, meaning keys match the API response names (e.g. "fooBar") rather than Python property names (e.g. foo_bar), and exclude_unset=True so fields not returned by the API are omitted.[1] BaseModel.to_dict(mode='json') serializes all values to JSON-safe types (e.g. datetime becomes the string "2024-3-22T18:11:19.117000Z"); mode='python' (the default) returns native Python objects.[1] BaseModel.to_json() generates an indented JSON string matching API field names, with indent=2 by default; pass indent=None for compact output.[1]
BaseModel.construct() / model_construct() supports recursive parsing without validation; model_construct is an alias for construct at runtime (type checkers see them as different).[1] The construct() method respects the populate_by_name (Pydantic v2) / allow_population_by_field_name (Pydantic v1) config option when resolving alias vs. field-name lookups.[1] Pydantic model schema build in src/anthropic/_models.py is deferred by default; set the DEFER_PYDANTIC_BUILD environment variable to false to build schemas eagerly (only applies to Pydantic v2).[1]
BaseModel._request_id exposes the request-id response header on the top-level response object only; accessing it on nested objects raises AttributeError.[1] Despite its _ prefix, BaseModel._request_id is a documented public property; all other _-prefixed attributes on SDK models are private.[1]
BaseAPIResponse in src/anthropic/_response.py exposes a retries_taken attribute counting the number of retries made for the request; it is 0 when no retries occurred.[2] BaseAPIResponse.elapsed returns the total datetime.timedelta for the complete request/response cycle.[2] APIResponse.request_id reads the request-id response header and returns it as str | None.[2] BaseAPIResponse.is_closed indicates whether the response body has been fully consumed; callers must either consume the body or call .close() to avoid resource leaks.[2] BaseAPIResponse caches parsed results per type in _parsed_by_type to avoid repeated deserialization of the same response body.[2] BaseAPIResponse.__repr__ renders as <ClassName [STATUS_CODE REASON] type=CAST_TO>.[2]
In src/anthropic/_response.py, _parse() unwraps both TypeAlias and Annotated wrappers before dispatching to the appropriate deserialization path.[2] The Content-Type header is split on ; before checking for json, so application/json; charset=utf-8 is accepted as a JSON response.[2] When parsing a non-JSON Content-Type response into a BaseModel, the SDK attempts to parse the body as JSON anyway; if that succeeds the parsed data is returned, otherwise it falls back to raw text (or raises if strict_response_validation is enabled).[2] _parse() supports primitive cast targets: str returns response.text, bytes returns response.content, int and float parse response.text, and bool compares response.text.lower() to "true".[2] SSE stream responses in src/anthropic/_response.py are dispatched to the configured _stream_cls; if none is set the client's _default_stream_cls is used, raising MissingStreamClassError when both are absent.[2] In src/anthropic/_files.py, when a file is supplied as a two-element tuple (content, mime_type) without a filename, the SDK derives a filename automatically to avoid malformed Content-Disposition headers in the multipart body. To control the filename explicitly in a multipart file upload, callers can pass a three-element tuple (filename, content, mime_type) instead of a two-element tuple, overriding any filename the SDK would derive.
Subclasses of httpx.Response cannot be passed as cast_to; only httpx.Response itself is accepted, raising ValueError otherwise.[2] Passing a Pydantic model that does not subclass the SDK's own BaseModel raises TypeError with the message: "Pydantic models must subclass our base model type, e.g. 'from anthropic import BaseModel'".[2] unwrap() in src/anthropic/resources/beta/webhooks.py requires a headers argument; omitting headers bypasses HMAC signature verification, allowing unverified payloads to be treated as authentic. UnwrapWebhookEvent in src/anthropic/types/beta/unwrap_webhook_event.py reflects the unwrap() contract that mandates headers for HMAC signature verification. In src/anthropic/types/beta/ and its sub-packages, header and path parameters must not carry wire alias annotations; such unused aliases are disallowed on param files (including agents, deployments, files, sessions, and workspaces).
Sources