# Actor events and state persistence

Copy for LLM

During its runtime, the Actor receives Actor events sent by the Apify platform or generated by the Apify SDK itself.

For details, see also [System events](https://docs.apify.com/platform/actors/development/programming-interface/system-events) and [State persistence](https://docs.apify.com/platform/actors/development/state-persistence).

## Event types[](#event-types)

A listener can optionally receive a single argument, a Pydantic model with the event's data. The following table lists the events, the type of that data object, and when each event is emitted.

| Event           | Data                                                                                                  | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| --------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SYSTEM_INFO`   | [`EventSystemInfoData`](https://docs.apify.com/sdk/python/reference/class/EventSystemInfoData.md)     | Emitted regularly to report the Actor's current resource usage. The `cpu_info.used_ratio` field reports the fraction of CPU currently in use (a float between `0.0` and `1.0`), and `memory_info.current_size` reports the current memory usage. Compare `cpu_info.used_ratio` against `Configuration.max_used_cpu_ratio` to detect CPU overload.                                                                                                                                                                                                                                                                                                                                                                     |
| `MIGRATING`     | [`EventMigratingData`](https://docs.apify.com/sdk/python/reference/class/EventMigratingData.md)       | Emitted when the Actor running on the Apify platform is going to be [migrated](https://docs.apify.com/platform/actors/development/state-persistence#what-is-a-migration) <!-- --><!-- -->to another worker server soon. The `time_remaining` field reports how much time the Actor has left before it is force-migrated.You can use it to persist the state of the Actor so that once it is executed again on the new server, it doesn't have to start over from the beginning. Once you have persisted the state of your Actor, you can call [`Actor.reboot`](https://docs.apify.com/sdk/python/reference/class/Actor.md#reboot) to reboot the Actor and trigger the migration immediately, to speed up the process. |
| `ABORTING`      | [`EventAbortingData`](https://docs.apify.com/sdk/python/reference/class/EventAbortingData.md)         | When a user aborts an Actor run on the Apify platform, they can choose to abort gracefully to allow the Actor some time before getting killed. This graceful abort emits the `ABORTING` event which you can use to finish all running tasks and do cleanup.                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `PERSIST_STATE` | [`EventPersistStateData`](https://docs.apify.com/sdk/python/reference/class/EventPersistStateData.md) | Emitted in regular intervals (by default 60 seconds) to notify the Actor that it should persist its state, in order to avoid repeating all work when the Actor restarts.This event is also emitted automatically when the `MIGRATING` event happens, in which case the `is_migrating` flag is set to `True`.Note that the `PERSIST_STATE` event is provided merely for user convenience, you can achieve the same effect by persisting the state regularly in an interval and listening for the migrating event.                                                                                                                                                                                                      |
| `EXIT`          | [`EventExitData`](https://docs.apify.com/sdk/python/reference/class/EventExitData.md)                 | Emitted by the SDK (not the platform) when the Actor is about to exit. You can use this event to perform final cleanup tasks, such as closing external connections or sending notifications, before the Actor shuts down.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |

## Adding handlers to events[](#adding-handlers-to-events)

To add handlers to these events, you use the [`Actor.on`](https://docs.apify.com/sdk/python/reference/class/Actor.md#on) method, and to remove them, you use the [`Actor.off`](https://docs.apify.com/sdk/python/reference/class/Actor.md#off) method.

[Run on](https://console.apify.com/actors/HH9rhkFXiZbheuq1V?runConfig=eyJ1IjoiRWdQdHczb2VqNlRhRHQ1cW4iLCJ2IjoxfQ.eyJpbnB1dCI6IntcImNvZGVcIjpcImltcG9ydCBhc3luY2lvXFxuXFxuZnJvbSBhcGlmeSBpbXBvcnQgQWN0b3IsIEV2ZW50LCBFdmVudFBlcnNpc3RTdGF0ZURhdGFcXG5cXG5cXG5hc3luYyBkZWYgbWFpbigpIC0-IE5vbmU6XFxuICAgIGFzeW5jIHdpdGggQWN0b3I6XFxuICAgICAgICB0b3RhbF9pdGVtcyA9IDEwMDBcXG5cXG4gICAgICAgICMgTG9hZCB0aGUgc3RhdGUgaWYgaXQncyBzYXZlZCBmcm9tIHNvbWUgcHJldmlvdXMgZXhlY3V0aW9uXFxuICAgICAgICBwcm9jZXNzZWRfaXRlbXMgPSAwXFxuICAgICAgICBhY3Rvcl9zdGF0ZSA9IGF3YWl0IEFjdG9yLmdldF92YWx1ZSgnU1RBVEUnKVxcbiAgICAgICAgaWYgYWN0b3Jfc3RhdGUgaXMgbm90IE5vbmU6XFxuICAgICAgICAgICAgcHJvY2Vzc2VkX2l0ZW1zID0gYWN0b3Jfc3RhdGVcXG5cXG4gICAgICAgICMgU2F2ZSB0aGUgc3RhdGUgd2hlbiB0aGUgYFBFUlNJU1RfU1RBVEVgIGV2ZW50IGhhcHBlbnNcXG4gICAgICAgIGFzeW5jIGRlZiBzYXZlX3N0YXRlKGV2ZW50X2RhdGE6IEV2ZW50UGVyc2lzdFN0YXRlRGF0YSkgLT4gTm9uZTpcXG4gICAgICAgICAgICBub25sb2NhbCBwcm9jZXNzZWRfaXRlbXNcXG4gICAgICAgICAgICBBY3Rvci5sb2cuaW5mbyhcXG4gICAgICAgICAgICAgICAgJ1BlcnNpc3RpbmcgQWN0b3Igc3RhdGUgKG1pZ3JhdGluZz0lcyknLCBldmVudF9kYXRhLmlzX21pZ3JhdGluZ1xcbiAgICAgICAgICAgIClcXG4gICAgICAgICAgICBhd2FpdCBBY3Rvci5zZXRfdmFsdWUoJ1NUQVRFJywgcHJvY2Vzc2VkX2l0ZW1zKVxcblxcbiAgICAgICAgQWN0b3Iub24oRXZlbnQuUEVSU0lTVF9TVEFURSwgc2F2ZV9zdGF0ZSlcXG5cXG4gICAgICAgICMgRG8gc29tZSBmYWtlIHdvcmtcXG4gICAgICAgIGZvciBpIGluIHJhbmdlKHByb2Nlc3NlZF9pdGVtcywgdG90YWxfaXRlbXMpOlxcbiAgICAgICAgICAgIEFjdG9yLmxvZy5pbmZvKGYnUHJvY2Vzc2luZyBpdGVtIHtpfS4uLicpXFxuICAgICAgICAgICAgcHJvY2Vzc2VkX2l0ZW1zID0gaVxcbiAgICAgICAgICAgIGF3YWl0IGFzeW5jaW8uc2xlZXAoMC4xKVxcblxcbiAgICAgICAgIyBTdXBwb3NlIHdlIGNhbiBzdG9wIHNhdmluZyB0aGUgc3RhdGUgbm93XFxuICAgICAgICBBY3Rvci5vZmYoRXZlbnQuUEVSU0lTVF9TVEFURSwgc2F2ZV9zdGF0ZSlcXG5cXG4gICAgICAgICMgRG8gc29tZSBtb3JlIGZha2Ugd29yaywgdGhpcyB0aW1lIHNvbWV0aGluZyB0aGF0IGNhbid0IGJlIHJlc3RhcnRlZCxcXG4gICAgICAgICMgc28gbm8gcG9pbnQgcGVyc2lzdGluZyB0aGUgc3RhdGVcXG4gICAgICAgIGZvciBqIGluIHJhbmdlKDEwKTpcXG4gICAgICAgICAgICBBY3Rvci5sb2cuaW5mbyhmJ1Byb2Nlc3NpbmcgaXRlbSB7an0gb2YgYW5vdGhlciBraW5kLi4uJylcXG4gICAgICAgICAgICBhd2FpdCBhc3luY2lvLnNsZWVwKDEpXFxuXFxuXFxuaWYgX19uYW1lX18gPT0gJ19fbWFpbl9fJzpcXG4gICAgYXN5bmNpby5ydW4obWFpbigpKVxcblwifSIsIm9wdGlvbnMiOnsiYnVpbGQiOiJsYXRlc3QiLCJjb250ZW50VHlwZSI6ImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLTgiLCJtZW1vcnkiOjEwMjQsInRpbWVvdXQiOjE4MH19.WqoxLp-rq_N0i3_mTCQJwIPnG0LHiPWcIJtUo_MyXxI\&asrc=run_on_apify)

```
import asyncio



from apify import Actor, Event, EventPersistStateData





async def main() -> None:

    async with Actor:

        total_items = 1000



        # Load the state if it's saved from some previous execution

        processed_items = 0

        actor_state = await Actor.get_value('STATE')

        if actor_state is not None:

            processed_items = actor_state



        # Save the state when the `PERSIST_STATE` event happens

        async def save_state(event_data: EventPersistStateData) -> None:

            nonlocal processed_items

            Actor.log.info(

                'Persisting Actor state (migrating=%s)', event_data.is_migrating

            )

            await Actor.set_value('STATE', processed_items)



        Actor.on(Event.PERSIST_STATE, save_state)



        # Do some fake work

        for i in range(processed_items, total_items):

            Actor.log.info(f'Processing item {i}...')

            processed_items = i

            await asyncio.sleep(0.1)



        # Suppose we can stop saving the state now

        Actor.off(Event.PERSIST_STATE, save_state)



        # Do some more fake work, this time something that can't be restarted,

        # so no point persisting the state

        for j in range(10):

            Actor.log.info(f'Processing item {j} of another kind...')

            await asyncio.sleep(1)





if __name__ == '__main__':

    asyncio.run(main())
```

## Automatic state persistence[](#automatic-state-persistence)

The example above shows how to manually persist state using the `PERSIST_STATE` event. For most use cases, you can use the [`Actor.use_state`](https://docs.apify.com/sdk/python/reference/class/Actor.md#use_state) method instead, which handles state persistence automatically.

`Actor.use_state` returns a dictionary that is automatically saved to the default key-value store at regular intervals and whenever a migration or shutdown occurs. You can modify the dictionary in place, and changes are persisted without any manual `set_value` calls.

You can optionally specify a `key` (the key-value store key under which the state is stored) and a `kvs_name` (the name of the key-value store to use). By default, the state is stored in the default key-value store under a default key.

[Run on](https://console.apify.com/actors/HH9rhkFXiZbheuq1V?runConfig=eyJ1IjoiRWdQdHczb2VqNlRhRHQ1cW4iLCJ2IjoxfQ.eyJpbnB1dCI6IntcImNvZGVcIjpcImltcG9ydCBhc3luY2lvXFxuXFxuZnJvbSBhcGlmeSBpbXBvcnQgQWN0b3JcXG5cXG5cXG5hc3luYyBkZWYgbWFpbigpIC0-IE5vbmU6XFxuICAgIGFzeW5jIHdpdGggQWN0b3I6XFxuICAgICAgICAjIEdldCBvciBjcmVhdGUgYW4gYXV0by1wZXJzaXN0ZWQgc3RhdGUgZGljdC5cXG4gICAgICAgICMgT24gcmVzdGFydCBvciBtaWdyYXRpb24sIHRoZSBzdGF0ZSBpcyBsb2FkZWQgZnJvbSB0aGUgS1ZTLlxcbiAgICAgICAgc3RhdGUgPSBhd2FpdCBBY3Rvci51c2Vfc3RhdGUoZGVmYXVsdF92YWx1ZT17J3Byb2Nlc3NlZF9pdGVtcyc6IDB9KVxcblxcbiAgICAgICAgIyBSZXN1bWUgZnJvbSB0aGUgcGVyc2lzdGVkIHN0YXRlIChzdG9yZWQgYXMgSlNPTiwgc28gbmFycm93IHRoZSB0eXBlKS5cXG4gICAgICAgIHN0YXJ0X2luZGV4ID0gc3RhdGVbJ3Byb2Nlc3NlZF9pdGVtcyddXFxuICAgICAgICBpZiBub3QgaXNpbnN0YW5jZShzdGFydF9pbmRleCwgaW50KTpcXG4gICAgICAgICAgICBzdGFydF9pbmRleCA9IDBcXG4gICAgICAgIEFjdG9yLmxvZy5pbmZvKGYnUmVzdW1pbmcgZnJvbSBpdGVtIHtzdGFydF9pbmRleH0nKVxcblxcbiAgICAgICAgIyBEbyBzb21lIHdvcmsgYW5kIHVwZGF0ZSB0aGUgc3RhdGUg4oCUIGl0IGlzIHBlcnNpc3RlZCBhdXRvbWF0aWNhbGx5XFxuICAgICAgICBmb3IgaSBpbiByYW5nZShzdGFydF9pbmRleCwgMTAwKTpcXG4gICAgICAgICAgICBBY3Rvci5sb2cuaW5mbyhmJ1Byb2Nlc3NpbmcgaXRlbSB7aX0uLi4nKVxcbiAgICAgICAgICAgIHN0YXRlWydwcm9jZXNzZWRfaXRlbXMnXSA9IGkgKyAxXFxuICAgICAgICAgICAgYXdhaXQgYXN5bmNpby5zbGVlcCgwLjEpXFxuXFxuXFxuaWYgX19uYW1lX18gPT0gJ19fbWFpbl9fJzpcXG4gICAgYXN5bmNpby5ydW4obWFpbigpKVxcblwifSIsIm9wdGlvbnMiOnsiYnVpbGQiOiJsYXRlc3QiLCJjb250ZW50VHlwZSI6ImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLTgiLCJtZW1vcnkiOjEwMjQsInRpbWVvdXQiOjE4MH19.B27snNyy0wMjL5-azgH96ccSvbWKh2TmiXqtaD8s6e8\&asrc=run_on_apify)

```
import asyncio



from apify import Actor





async def main() -> None:

    async with Actor:

        # Get or create an auto-persisted state dict.

        # On restart or migration, the state is loaded from the KVS.

        state = await Actor.use_state(default_value={'processed_items': 0})



        # Resume from the persisted state (stored as JSON, so narrow the type).

        start_index = state['processed_items']

        if not isinstance(start_index, int):

            start_index = 0

        Actor.log.info(f'Resuming from item {start_index}')



        # Do some work and update the state — it is persisted automatically

        for i in range(start_index, 100):

            Actor.log.info(f'Processing item {i}...')

            state['processed_items'] = i + 1

            await asyncio.sleep(0.1)





if __name__ == '__main__':

    asyncio.run(main())
```
