> ## Documentation Index
> Fetch the complete documentation index at: https://docs.notte.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# AsyncNotteClient

> Call the Notte API from asyncio code with the async Python client (preview)

> [!TIP]
>
> Read [the Notte skill](https://notte.cc/skill.md) and follow its setup instructions when setting up Notte or building browser automation. This is the shared source for CLI setup, authentication, and the browser-to-code workflow.

> [!NOTE]
>
> Available from `notte-sdk` 1.10.0. `AsyncNotteClient` is in preview: its API may still change.

`AsyncNotteClient` is the asyncio version of [`NotteClient`](/sdk-reference/manual/index.md). Use it when your code already runs in an event loop (an async web server or agent framework, for example), or to run sessions, scrapes and functions concurrently without threads. In synchronous code, keep using `NotteClient`.

It takes the same constructor arguments as `NotteClient` and reads `NOTTE_API_KEY` the same way. Its resources (`sessions`, `functions`, `files`, `personas`, `profiles`, `vaults` and `managed_auth`), `health_check()` and `scrape()` have the same methods as on `NotteClient`, with the same names, arguments, results and exceptions, as coroutines: add `await`. `client.Session`, `client.Function`, `client.Persona`, `client.Vault` and `client.FileStorage` return async versions of the same objects.

## Open and close the client

```python async_client.py theme={null}
import asyncio

from notte_sdk import AsyncNotteClient


async def main() -> None:
    # `async with` closes the client's connections on exit
    async with AsyncNotteClient() as client:
        await client.health_check()

    # without `async with`, close the client yourself
    client = AsyncNotteClient()
    try:
        await client.health_check()
    finally:
        await client.aclose()


asyncio.run(main())
```

Connections open on the first request. Close them with `async with` or `await client.aclose()`. They cannot be closed when the client is garbage collected, so an unclosed client emits a `ResourceWarning` instead. After `aclose()`, the client stays usable: the next request opens new connections.

## Sessions

```python async_session.py theme={null}
import asyncio

from notte_sdk import AsyncNotteClient


async def main() -> None:
    async with AsyncNotteClient() as client:
        # the session is stopped when the block exits, also on errors and cancellation
        async with client.Session() as session:
            _ = await session.execute(type="goto", url="https://example.com")
            markdown = await session.scrape()
            print(markdown)

            # the async Playwright page, connected to the same browser
            page = await session.page
            print(await page.title())


asyncio.run(main())
```

* `async with client.Session()` starts the session and stops it when the block exits, also when entering the block fails or the task is cancelled. There is no sync `with` block.
* `await session.page` returns the async Playwright page. It needs `pip install "notte-sdk[playwright]"`.
* `client.Session(session_id)` attaches to an existing session without sending a request. The first method that needs the session's state reads it.

## Scrape

```python async_scrape.py theme={null}
import asyncio

from notte_sdk import AsyncNotteClient

urls = ["https://example.com", "https://www.notte.cc"]


async def main() -> None:
    async with AsyncNotteClient() as client:
        # each scrape starts and stops its own session. The TaskGroup runs them concurrently and
        # waits for every one, cleanup included, before the client closes.
        async with asyncio.TaskGroup() as group:
            tasks = [group.create_task(client.scrape(url)) for url in urls]
        for url, task in zip(urls, tasks):
            print(url, task.result()[:100])


asyncio.run(main())
```

`await client.scrape(url, ...)` takes the same arguments as `NotteClient.scrape`, including `response_format` and `instructions`. It starts a session, scrapes the page and stops the session, also when the scrape fails.

## Functions

```python async_function.py theme={null}
import asyncio

from notte_sdk import AsyncNotteClient


async def main() -> None:
    async with AsyncNotteClient() as client:
        # creating a function sends a request, so await it
        function = await client.Function(path="my_function.py", name="Async example")
        try:
            # loading a function by ID sends no request until you use it
            same_function = client.Function(function_id=function.function_id)
            result = await same_function.run(url="https://example.com")
            print(result.status, result.result)
        finally:
            await function.delete()


asyncio.run(main())
```

`my_function.py` is a function file as described in [Creating Functions](/features/functions/creating.md). `function.run()` takes the same arguments as the sync `run()`.

## Personas and vaults

The sync `Persona` and `Vault` objects call the API in their constructor. An async constructor cannot, so `await` the object to create it, or to load it when you pass an ID: `persona = await client.Persona(persona_id)`. `async with` does the same and also deletes it on exit.

```python async_persona_vault.py theme={null}
import asyncio

from notte_sdk import AsyncNotteClient


async def main() -> None:
    async with AsyncNotteClient() as client:
        # `async with` creates the persona and deletes it on exit
        async with client.Persona(create_vault=True) as persona:
            print(f"Persona email: {persona.info.email}")
            await persona.add_credentials(url="https://github.com/")
            emails = await persona.emails(only_unread=True, limit=10)
            print(f"Recent emails: {emails}")

        # `await` creates the vault; delete it when you no longer need it
        vault = await client.Vault(name="async-example")
        try:
            password = vault.generate_password()  # no request, so not awaited
            await vault.add_credentials(url="https://example.com", email="me@example.com", password=password)
            print(await vault.list_credentials())
        finally:
            await vault.delete()


asyncio.run(main())
```

## Event loops and files

* A client's connections belong to the event loop that made its first request. Using the client from another event loop while that loop is still open raises `RuntimeError`, so create one client per event loop. Once the first loop is closed, the next request opens new connections in the current loop.
* Local files, such as function code, uploads, downloads and cookie files, are read and written off the event loop.

## What is not async

* Methods that send no request stay plain methods: `client.functions.get_curl()`, `client.functions.decode_message()` and `vault.generate_password()`, for example.
* The deprecated notte 1.9 names, such as `client.workflows`, `client.Workflow` and `client.functions.run()`, exist only on `NotteClient`. Use the current names.
* `RemoteSession.apage` and `RemoteSession.await_for_auth()` have no async counterpart: on an async session, `session.page` and `session.wait_for_auth()` are already awaitable.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.