---
title: Handling Actor events & persisting state
sidebar_label: Actor events & state persistence
---

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

## Event types

<!-- markdownlint-disable no-inline-html -->
<table>
    <thead>
        <tr>
            <th>Event</th>
            <th>Data</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>SYSTEM_INFO</code></td>
            <td><pre>{`{
  "createdAt": datetime,
  "cpuCurrentUsage": float,
  "memCurrentBytes": int,
  "isCpuOverloaded": bool
}`}
            </pre></td>
            <td>
                <p>This event is emitted regularly and it indicates the current resource usage of the Actor.</p>
                The <code>isCpuOverloaded</code> argument indicates whether the current CPU usage is higher than <code>Config.max_used_cpu_ratio</code>
            </td>
        </tr>
        <tr>
            <td><code>MIGRATING</code></td>
            <td><code>None</code></td>
            <td>
                <p>Emitted when the Actor running on the Apify platform
                is going to be <a href="https://docs.apify.com/platform/actors/development/state-persistence#what-is-a-migration">migrated</a>
                {' '}to another worker server soon.</p>
                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.
            </td>
        </tr>
        <tr>
            <td><code>ABORTING</code></td>
            <td><code>None</code></td>
            <td>
                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 <code>ABORTING</code> event which you can use to finish all running tasks and do cleanup.
            </td>
        </tr>
        <tr>
            <td><code>PERSIST_STATE</code></td>
            <td><pre>{`{ "isMigrating": bool }`}</pre></td>
            <td>
                <p>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.</p>
                <p>This event is also emitted automatically when the <code>MIGRATING</code> event happens,
                in which case the <code>isMigrating</code> flag is set to <code>True</code>.</p>
                Note that the <code>PERSIST_STATE</code> 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.
            </td>
        </tr>
    </tbody>
</table>

<!-- markdownlint-enable no-inline-html -->

## Adding handlers to events

To add handlers to these events, you use the [`Actor.on()`](../../reference/class/Actor#on) method,
and to remove them, you use the [`Actor.off()`](../../reference/class/Actor#off) method.

```python title="src/main.py"
import asyncio
from apify import Actor
from apify_shared.consts import ActorEventTypes

async def main():
    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):
            nonlocal processed_items
            Actor.log.info('Saving actor state', extra=event_data)
            await Actor.set_value('STATE', processed_items)

        Actor.on(ActorEventTypes.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(ActorEventTypes.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(0, 10):
            Actor.log.info(f'Processing item {j} of another kind...')
            await asyncio.sleep(1)
```
