# Typed models

Copy for LLM

Resource client methods return [Pydantic](https://docs.pydantic.dev/) models generated directly from the [Apify OpenAPI specification](https://docs.apify.com/api/openapi.json). You get IDE autocompletion, runtime validation of API responses, and a Python-idiomatic snake\_case interface on top of the underlying camelCase API.

## Accessing response fields[](#accessing-response-fields)

Every method that returns a structured payload returns a Pydantic model. Fields are accessed using their Python snake\_case names regardless of the camelCase used by the API, and the static type of each field comes through to your editor.

* Async client
* Sync client

```
from apify_client import ApifyClientAsync



TOKEN = 'MY-APIFY-TOKEN'





async def main() -> None:

    apify_client = ApifyClientAsync(TOKEN)



    # `get` returns an `Actor` Pydantic model — fields are typed and IDE-completable.

    actor = await apify_client.actor('apify/hello-world').get()

    if actor is None:

        return



    print(actor.id)  # str

    print(actor.username)  # str

    print(actor.is_public)  # bool

    print(actor.created_at)  # datetime.datetime (timezone-aware)

    print(actor.stats.total_runs)  # int — nested model, attribute access all the way down
```

```
from apify_client import ApifyClient



TOKEN = 'MY-APIFY-TOKEN'





def main() -> None:

    apify_client = ApifyClient(TOKEN)



    # `get` returns an `Actor` Pydantic model — fields are typed and IDE-completable.

    actor = apify_client.actor('apify/hello-world').get()

    if actor is None:

        return



    print(actor.id)  # str

    print(actor.username)  # str

    print(actor.is_public)  # bool

    print(actor.created_at)  # datetime.datetime (timezone-aware)

    print(actor.stats.total_runs)  # int — nested model, attribute access all the way down
```

Date strings are automatically parsed into timezone-aware `datetime.datetime` objects, enums into `Literal` aliases, and nested objects into their own typed models, so you can compose attribute access without manual conversion.

## Providing structured input[](#providing-structured-input)

A Pydantic model returned from one client call can be passed directly into any other method that accepts the same shape — useful for round-trip flows where you read a resource, tweak it, and write it back.

For input you construct yourself, plain dictionaries work on every input method. Each input shape has a matching [`TypedDict`](https://docs.python.org/3/library/typing.html#typing.TypedDict) that documents the expected keys.

* Async client
* Sync client

```
from apify_client import ApifyClientAsync



TOKEN = 'MY-APIFY-TOKEN'





async def main() -> None:

    apify_client = ApifyClientAsync(TOKEN)

    rq_client = apify_client.request_queue('REQUEST-QUEUE-ID')



    # Plain dict — keys may be snake_case or camelCase.

    await rq_client.add_request(

        {

            'url': 'https://example.com',

            'unique_key': 'https://example.com',

            'method': 'GET',

        }

    )
```

```
from apify_client import ApifyClient



TOKEN = 'MY-APIFY-TOKEN'





def main() -> None:

    apify_client = ApifyClient(TOKEN)

    rq_client = apify_client.request_queue('REQUEST-QUEUE-ID')



    # Plain dict — keys may be snake_case or camelCase.

    rq_client.add_request(

        {

            'url': 'https://example.com',

            'unique_key': 'https://example.com',

            'method': 'GET',

        }

    )
```

## Forward compatibility[](#forward-compatibility)

Generated models are configured with `extra='allow'`. Any new fields the API starts returning in the future are preserved on the model instance — they simply do not yet have a typed attribute. Upgrading the client to pick up a newer OpenAPI spec is a non-breaking change for code that reads existing fields.

## Browsing all models[](#browsing-all-models)

The full list of generated models and TypedDicts is available in the [API reference](https://docs.apify.com/api/client/python/api/client/python/reference.md) under the **Models** and **Typed dicts** groups.

## Methods that return plain types[](#methods-that-return-plain-types)

A few endpoints intentionally return plain Python types instead of Pydantic models because their payloads are user-defined or inherently unstructured:

* [`DatasetClient.list_items()`](https://docs.apify.com/api/client/python/api/client/python/reference/class/DatasetClient.md#list_items) returns a [`DatasetItemsPage`](https://docs.apify.com/api/client/python/api/client/python/reference/class/DatasetItemsPage.md) whose `items` field is `list[dict[str, Any]]`. Dataset items follow the [Actor output schema](https://docs.apify.com/platform/actors/development/actor-definition/output-schema), which the client cannot know in advance.
* [`KeyValueStoreClient.get_record()`](https://docs.apify.com/api/client/python/api/client/python/reference/class/KeyValueStoreClient.md#get_record) returns a `dict` with `key`, `value`, and `content_type` keys. The shape of `value` is determined by the record's content type.

For background on the migration from plain dicts to typed models, see [Upgrading to v3](https://docs.apify.com/api/client/python/api/client/python/docs/upgrading/upgrading-to-v3.md).
