From 47a49a2eb7eae7c377331dfc9c3d5c5ea6442e6d Mon Sep 17 00:00:00 2001 From: szaganek Date: Mon, 27 Jul 2026 15:36:35 +0200 Subject: [PATCH 1/2] docs: Update the PPE monetization guide --- docs/02_concepts/08_pay_per_event.mdx | 77 ++++++++++--------- docs/02_concepts/code/charging_manager.ts | 17 ++++ .../02_concepts/08_pay_per_event.mdx | 77 ++++++++++--------- .../02_concepts/code/charging_manager.ts | 17 ++++ 4 files changed, 116 insertions(+), 72 deletions(-) create mode 100644 docs/02_concepts/code/charging_manager.ts create mode 100644 website/versioned_docs/version-3.7/02_concepts/code/charging_manager.ts diff --git a/docs/02_concepts/08_pay_per_event.mdx b/docs/02_concepts/08_pay_per_event.mdx index 63808c254a..fd5c9e6ce4 100644 --- a/docs/02_concepts/08_pay_per_event.mdx +++ b/docs/02_concepts/08_pay_per_event.mdx @@ -1,78 +1,83 @@ --- id: pay-per-event -title: Pay-per-event Monetization -description: Monetize your Actors using the pay-per-event pricing model +title: Pay-per-event monetization +description: Monetize your Actors using the pay-per-event pricing model. --- import ActorChargeSource from '!!raw-loader!./code/actor_charge.ts'; import ConditionalActorChargeSource from '!!raw-loader!./code/conditional_actor_charge.ts'; import ChargeLimitCheckSource from '!!raw-loader!./code/charge_limit_check.ts'; import PerUnitChargingSource from '!!raw-loader!./code/per_unit_charging.ts'; +import ChargingManagerSource from '!!raw-loader!./code/charging_manager.ts'; import ApiLink from '@site/src/components/ApiLink'; import CodeBlock from '@theme/CodeBlock'; -Apify provides several [pricing models](https://docs.apify.com/platform/actors/publishing/monetize) for monetizing your Actors. The most recent and most flexible one is [pay-per-event](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event), which lets you charge your users programmatically directly from your Actor. As the name suggests, you may charge the users each time a specific event occurs, for example a call to an external API or when you return a result. +With the [pay-per-event pricing model](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event), users pay for specific events that are programmatically triggered from your Actor's source code. Such events might include, for example, generating a single result or calling an external API. -To use the pay-per-event pricing model, you first need to [set it up](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event) for your Actor in the Apify console. After that, you're free to start charging for events. +## Configure monetization -:::info How pay-per-event pricing works +To use the pay-per-event pricing model and define the pricing, [monetize your Actor](https://docs.apify.com/platform/actors/publishing/monetize) in Apify Console. -If you want more details about PPE pricing, please refer to our [PPE documentation](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event). +## Charge for events -::: - -## Charging for events - -After monetization is set in the Apify console, you can add `Actor.charge` calls to your code and start monetizing! +To charge for events, use the `Actor.charge` method. It records that your Actor performed a billable activity, so the Apify platform charges the user's account for it. {ActorChargeSource} -Then you just push your code to Apify and that's it! The SDK will even keep track of the max total charge setting for you, so you will not provide more value than what the user chose to pay for. - -If you need finer control over charging, you can access call `Actor.getChargingManager()` to access the `ChargingManager`, which can provide more detailed information - for example how many events of each type can be charged before reaching the configured limit. +For details on how to maximize your profits, see also [Best practices](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#best-practices). -### Handling the charge limit +### Prefer per-unit charging over batching -While the SDK automatically prevents overcharging by limiting how many events are charged and how many items are pushed, **it does not stop your Actor from running**. When the charge limit is reached, `Actor.charge` and `Actor.pushData` will silently stop charging and pushing data, but your Actor will keep running — potentially doing expensive work (scraping pages, calling APIs) for no purpose. This means your Actor may never terminate on its own if you don't check the charge limit yourself. +If you can split your work into individual units, for example scraping one page or calling one API endpoint, prefer issuing one `Actor.charge()` call per unit. Don't batch multiple events into a single call with the `count` parameter. This approach gives you better control over budget consumption: -To avoid this, you should check the `eventChargeLimitReached` field in the result returned by `Actor.charge` or `Actor.pushData` and stop your Actor when the limit is reached. You can also use the `chargeableWithinLimit` field from the result to plan ahead — it tells you how many events of each type can still be charged within the remaining budget. +{PerUnitChargingSource} -{ChargeLimitCheckSource} +If you use the `count` parameter, always check the returned `chargedCount`. It tells you how many events were charged, which may be less than what you requested. -Alternatively, you can periodically check the remaining budget via `Actor.getChargingManager()` instead of inspecting every `ChargeResult`. This can be useful when charging happens in multiple places across your code, or when using a crawler where you don't directly control the main loop. +### Monitor charging -:::caution -Always check the charge limit in your Actor, whether through `ChargeResult` return values or the `ChargingManager`. Without this check, your Actor will continue running and consuming platform resources after the budget is exhausted, producing no output. -::: +For custom events, every `Actor.charge` call returns `ChargeResult`. Inspect its fields to learn how much was charged. -## Best practices for charging +Instead of inspecting every `ChargeResult`, you can also use the `ChargingManager`. It provides methods for querying the remaining budget, total charged amount, and per-event charge counts. -### Prefer per-unit charging over batching +This information lets you plan work based on the remaining budget rather than discovering the limit after the fact. It's particularly useful when charging happens in multiple places across your code, or when using a crawler where you don't directly control the main loop. -When your work can be split into individual units (e.g. scraping one page, calling one API endpoint), prefer issuing one `Actor.charge()` call per unit rather than batching multiple events into a single call via the `count` parameter. This gives you finer control over budget consumption: +To access the `ChargingManager`, use the `Actor.getChargingManager()` method: -{PerUnitChargingSource} +{ChargingManagerSource} -If you do use the `count` parameter, always check the returned `chargedCount` — it tells you how many events were actually charged, which may be less than what you requested. +### Handle the charge limit -### Always check the charge limit +Your Actor users can set the maximum cost for the run. It helps users control their spending, as they won't be billed beyond the limit. -Both `Actor.charge` and `Actor.pushData` return a `ChargeResult` object. Always inspect `eventChargeLimitReached` or `chargedCount` and stop doing work when the budget is exhausted. Without this check, your Actor will continue consuming platform resources without producing value for the user. +The user spending limit for the run is available in your Actor code as the `ACTOR_MAX_TOTAL_CHARGE_USD` environment variable, and `ChargeResult` already accounts for it. To control the limit, inspect the fields of `ChargeResult`: -## Transitioning from a different pricing model +- `eventChargeLimitReached`: Checks if the user's limit allows for another charge of the event. +- `chargeableWithinLimit`: Indicates how many events of each type can still be charged within the remaining budget. +- `chargedCount`: Indicates how many events were billed by the call. -When you plan to start using the pay-per-event pricing model for an Actor that is already monetized with a different pricing model, your source code will need support both pricing models during the transition period enforced by the Apify platform. Arguably the most frequent case is the transition from the pay-per-result model which utilizes the `ACTOR_MAX_PAID_DATASET_ITEMS` environment variable to prevent returning unpaid dataset items. The following is an example how to handle such scenarios. The key part is the `ChargingManager.getPricingInfo` method which returns information about the current pricing model. +{ChargeLimitCheckSource} -{ConditionalActorChargeSource} +When the charge limit is reached, `Actor.charge` stops charging and `Actor.push_data` stops pushing data. The platform then aborts the run automatically. However, the run keeps consuming platform resources for a short time before it stops. For details, see [Handle graceful shutdown](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#handle-graceful-shutdown). -## Local development +## Test monetization locally -It is encouraged to test your monetization code on your machine before releasing it to the public. To tell your Actor that it should work in pay-per-event mode, pass it the `ACTOR_TEST_PAY_PER_EVENT` environment variable: +Before releasing your monetization code to the public, test it locally. To make your Actor work in pay-per-event mode, pass it the `ACTOR_TEST_PAY_PER_EVENT` environment variable: ```shell ACTOR_TEST_PAY_PER_EVENT=true npm start ``` -If you also wish to see a log of all the events charged throughout the run, you also need to pass the `ACTOR_USE_CHARGING_LOG_DATASET` environment variable. Your charging dataset will then be available under the `charging_log` name (unless you change your storage settings, this dataset is stored in `storage/datasets/charging_log/`). Please note that this log is not available when running the Actor in production on the Apify platform. +### View the log + +To inspect the results of your tests, open the `charging-log` dataset. By default, it's stored in the `storage/datasets/charging-log/` directory. + +The log contains all the events charged throughout the run. Because pricing configuration is stored by the Apify platform, all events have a default price of $1. + +Note that this log isn't available when running the Actor in production on the Apify platform. -Because pricing configuration is stored by the Apify platform, all events will have a default price of $1. +## Transitioning from a different pricing model + +When you plan to start using the pay-per-event pricing model for an Actor that is already monetized with a different pricing model, your source code will need support both pricing models during the transition period enforced by the Apify platform. Arguably the most frequent case is the transition from the pay-per-result model which utilizes the `ACTOR_MAX_PAID_DATASET_ITEMS` environment variable to prevent returning unpaid dataset items. The following is an example how to handle such scenarios. The key part is the `ChargingManager.getPricingInfo` method which returns information about the current pricing model. + +{ConditionalActorChargeSource} diff --git a/docs/02_concepts/code/charging_manager.ts b/docs/02_concepts/code/charging_manager.ts new file mode 100644 index 0000000000..d2e07edd15 --- /dev/null +++ b/docs/02_concepts/code/charging_manager.ts @@ -0,0 +1,17 @@ +import { Actor } from 'apify'; + +await Actor.init(); + +const chargingManager = Actor.getChargingManager(); + +// Check the total budget for this run +const maxCharge = chargingManager.getMaxTotalChargeUsd(); +console.log(`Max charge: ${maxCharge}`); + +// Check how many events can still be charged before reaching the limit +const remainingCharge = chargingManager.calculateMaxEventChargeCountWithinLimit( + 'result-item', +); +console.log(`Remaining number of events: ${remainingCharge}`); + +await Actor.exit(); diff --git a/website/versioned_docs/version-3.7/02_concepts/08_pay_per_event.mdx b/website/versioned_docs/version-3.7/02_concepts/08_pay_per_event.mdx index 63808c254a..fd5c9e6ce4 100644 --- a/website/versioned_docs/version-3.7/02_concepts/08_pay_per_event.mdx +++ b/website/versioned_docs/version-3.7/02_concepts/08_pay_per_event.mdx @@ -1,78 +1,83 @@ --- id: pay-per-event -title: Pay-per-event Monetization -description: Monetize your Actors using the pay-per-event pricing model +title: Pay-per-event monetization +description: Monetize your Actors using the pay-per-event pricing model. --- import ActorChargeSource from '!!raw-loader!./code/actor_charge.ts'; import ConditionalActorChargeSource from '!!raw-loader!./code/conditional_actor_charge.ts'; import ChargeLimitCheckSource from '!!raw-loader!./code/charge_limit_check.ts'; import PerUnitChargingSource from '!!raw-loader!./code/per_unit_charging.ts'; +import ChargingManagerSource from '!!raw-loader!./code/charging_manager.ts'; import ApiLink from '@site/src/components/ApiLink'; import CodeBlock from '@theme/CodeBlock'; -Apify provides several [pricing models](https://docs.apify.com/platform/actors/publishing/monetize) for monetizing your Actors. The most recent and most flexible one is [pay-per-event](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event), which lets you charge your users programmatically directly from your Actor. As the name suggests, you may charge the users each time a specific event occurs, for example a call to an external API or when you return a result. +With the [pay-per-event pricing model](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event), users pay for specific events that are programmatically triggered from your Actor's source code. Such events might include, for example, generating a single result or calling an external API. -To use the pay-per-event pricing model, you first need to [set it up](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event) for your Actor in the Apify console. After that, you're free to start charging for events. +## Configure monetization -:::info How pay-per-event pricing works +To use the pay-per-event pricing model and define the pricing, [monetize your Actor](https://docs.apify.com/platform/actors/publishing/monetize) in Apify Console. -If you want more details about PPE pricing, please refer to our [PPE documentation](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event). +## Charge for events -::: - -## Charging for events - -After monetization is set in the Apify console, you can add `Actor.charge` calls to your code and start monetizing! +To charge for events, use the `Actor.charge` method. It records that your Actor performed a billable activity, so the Apify platform charges the user's account for it. {ActorChargeSource} -Then you just push your code to Apify and that's it! The SDK will even keep track of the max total charge setting for you, so you will not provide more value than what the user chose to pay for. - -If you need finer control over charging, you can access call `Actor.getChargingManager()` to access the `ChargingManager`, which can provide more detailed information - for example how many events of each type can be charged before reaching the configured limit. +For details on how to maximize your profits, see also [Best practices](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#best-practices). -### Handling the charge limit +### Prefer per-unit charging over batching -While the SDK automatically prevents overcharging by limiting how many events are charged and how many items are pushed, **it does not stop your Actor from running**. When the charge limit is reached, `Actor.charge` and `Actor.pushData` will silently stop charging and pushing data, but your Actor will keep running — potentially doing expensive work (scraping pages, calling APIs) for no purpose. This means your Actor may never terminate on its own if you don't check the charge limit yourself. +If you can split your work into individual units, for example scraping one page or calling one API endpoint, prefer issuing one `Actor.charge()` call per unit. Don't batch multiple events into a single call with the `count` parameter. This approach gives you better control over budget consumption: -To avoid this, you should check the `eventChargeLimitReached` field in the result returned by `Actor.charge` or `Actor.pushData` and stop your Actor when the limit is reached. You can also use the `chargeableWithinLimit` field from the result to plan ahead — it tells you how many events of each type can still be charged within the remaining budget. +{PerUnitChargingSource} -{ChargeLimitCheckSource} +If you use the `count` parameter, always check the returned `chargedCount`. It tells you how many events were charged, which may be less than what you requested. -Alternatively, you can periodically check the remaining budget via `Actor.getChargingManager()` instead of inspecting every `ChargeResult`. This can be useful when charging happens in multiple places across your code, or when using a crawler where you don't directly control the main loop. +### Monitor charging -:::caution -Always check the charge limit in your Actor, whether through `ChargeResult` return values or the `ChargingManager`. Without this check, your Actor will continue running and consuming platform resources after the budget is exhausted, producing no output. -::: +For custom events, every `Actor.charge` call returns `ChargeResult`. Inspect its fields to learn how much was charged. -## Best practices for charging +Instead of inspecting every `ChargeResult`, you can also use the `ChargingManager`. It provides methods for querying the remaining budget, total charged amount, and per-event charge counts. -### Prefer per-unit charging over batching +This information lets you plan work based on the remaining budget rather than discovering the limit after the fact. It's particularly useful when charging happens in multiple places across your code, or when using a crawler where you don't directly control the main loop. -When your work can be split into individual units (e.g. scraping one page, calling one API endpoint), prefer issuing one `Actor.charge()` call per unit rather than batching multiple events into a single call via the `count` parameter. This gives you finer control over budget consumption: +To access the `ChargingManager`, use the `Actor.getChargingManager()` method: -{PerUnitChargingSource} +{ChargingManagerSource} -If you do use the `count` parameter, always check the returned `chargedCount` — it tells you how many events were actually charged, which may be less than what you requested. +### Handle the charge limit -### Always check the charge limit +Your Actor users can set the maximum cost for the run. It helps users control their spending, as they won't be billed beyond the limit. -Both `Actor.charge` and `Actor.pushData` return a `ChargeResult` object. Always inspect `eventChargeLimitReached` or `chargedCount` and stop doing work when the budget is exhausted. Without this check, your Actor will continue consuming platform resources without producing value for the user. +The user spending limit for the run is available in your Actor code as the `ACTOR_MAX_TOTAL_CHARGE_USD` environment variable, and `ChargeResult` already accounts for it. To control the limit, inspect the fields of `ChargeResult`: -## Transitioning from a different pricing model +- `eventChargeLimitReached`: Checks if the user's limit allows for another charge of the event. +- `chargeableWithinLimit`: Indicates how many events of each type can still be charged within the remaining budget. +- `chargedCount`: Indicates how many events were billed by the call. -When you plan to start using the pay-per-event pricing model for an Actor that is already monetized with a different pricing model, your source code will need support both pricing models during the transition period enforced by the Apify platform. Arguably the most frequent case is the transition from the pay-per-result model which utilizes the `ACTOR_MAX_PAID_DATASET_ITEMS` environment variable to prevent returning unpaid dataset items. The following is an example how to handle such scenarios. The key part is the `ChargingManager.getPricingInfo` method which returns information about the current pricing model. +{ChargeLimitCheckSource} -{ConditionalActorChargeSource} +When the charge limit is reached, `Actor.charge` stops charging and `Actor.push_data` stops pushing data. The platform then aborts the run automatically. However, the run keeps consuming platform resources for a short time before it stops. For details, see [Handle graceful shutdown](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#handle-graceful-shutdown). -## Local development +## Test monetization locally -It is encouraged to test your monetization code on your machine before releasing it to the public. To tell your Actor that it should work in pay-per-event mode, pass it the `ACTOR_TEST_PAY_PER_EVENT` environment variable: +Before releasing your monetization code to the public, test it locally. To make your Actor work in pay-per-event mode, pass it the `ACTOR_TEST_PAY_PER_EVENT` environment variable: ```shell ACTOR_TEST_PAY_PER_EVENT=true npm start ``` -If you also wish to see a log of all the events charged throughout the run, you also need to pass the `ACTOR_USE_CHARGING_LOG_DATASET` environment variable. Your charging dataset will then be available under the `charging_log` name (unless you change your storage settings, this dataset is stored in `storage/datasets/charging_log/`). Please note that this log is not available when running the Actor in production on the Apify platform. +### View the log + +To inspect the results of your tests, open the `charging-log` dataset. By default, it's stored in the `storage/datasets/charging-log/` directory. + +The log contains all the events charged throughout the run. Because pricing configuration is stored by the Apify platform, all events have a default price of $1. + +Note that this log isn't available when running the Actor in production on the Apify platform. -Because pricing configuration is stored by the Apify platform, all events will have a default price of $1. +## Transitioning from a different pricing model + +When you plan to start using the pay-per-event pricing model for an Actor that is already monetized with a different pricing model, your source code will need support both pricing models during the transition period enforced by the Apify platform. Arguably the most frequent case is the transition from the pay-per-result model which utilizes the `ACTOR_MAX_PAID_DATASET_ITEMS` environment variable to prevent returning unpaid dataset items. The following is an example how to handle such scenarios. The key part is the `ChargingManager.getPricingInfo` method which returns information about the current pricing model. + +{ConditionalActorChargeSource} diff --git a/website/versioned_docs/version-3.7/02_concepts/code/charging_manager.ts b/website/versioned_docs/version-3.7/02_concepts/code/charging_manager.ts new file mode 100644 index 0000000000..d2e07edd15 --- /dev/null +++ b/website/versioned_docs/version-3.7/02_concepts/code/charging_manager.ts @@ -0,0 +1,17 @@ +import { Actor } from 'apify'; + +await Actor.init(); + +const chargingManager = Actor.getChargingManager(); + +// Check the total budget for this run +const maxCharge = chargingManager.getMaxTotalChargeUsd(); +console.log(`Max charge: ${maxCharge}`); + +// Check how many events can still be charged before reaching the limit +const remainingCharge = chargingManager.calculateMaxEventChargeCountWithinLimit( + 'result-item', +); +console.log(`Remaining number of events: ${remainingCharge}`); + +await Actor.exit(); From 4ac54a37fb1bec557eb549bad18a373788a7c484 Mon Sep 17 00:00:00 2001 From: szaganek Date: Tue, 28 Jul 2026 14:36:34 +0200 Subject: [PATCH 2/2] Apply feedback --- docs/02_concepts/08_pay_per_event.mdx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/02_concepts/08_pay_per_event.mdx b/docs/02_concepts/08_pay_per_event.mdx index fd5c9e6ce4..edf915ec33 100644 --- a/docs/02_concepts/08_pay_per_event.mdx +++ b/docs/02_concepts/08_pay_per_event.mdx @@ -52,13 +52,13 @@ Your Actor users can set the maximum cost for the run. It helps users control th The user spending limit for the run is available in your Actor code as the `ACTOR_MAX_TOTAL_CHARGE_USD` environment variable, and `ChargeResult` already accounts for it. To control the limit, inspect the fields of `ChargeResult`: -- `eventChargeLimitReached`: Checks if the user's limit allows for another charge of the event. +- `eventChargeLimitReached`: Checks if the user's spending limits has been reached and doesn't allow for another charge of the event. - `chargeableWithinLimit`: Indicates how many events of each type can still be charged within the remaining budget. - `chargedCount`: Indicates how many events were billed by the call. {ChargeLimitCheckSource} -When the charge limit is reached, `Actor.charge` stops charging and `Actor.push_data` stops pushing data. The platform then aborts the run automatically. However, the run keeps consuming platform resources for a short time before it stops. For details, see [Handle graceful shutdown](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#handle-graceful-shutdown). +When the charge limit is reached, `Actor.charge` stops charging and `Actor.pushData` stops pushing data. The platform then aborts the run automatically. However, the run keeps consuming platform resources for a short time before it stops. For details, see [Handle graceful shutdown](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event#handle-graceful-shutdown). ## Test monetization locally @@ -70,7 +70,15 @@ ACTOR_TEST_PAY_PER_EVENT=true npm start ### View the log -To inspect the results of your tests, open the `charging-log` dataset. By default, it's stored in the `storage/datasets/charging-log/` directory. +To inspect the results of your tests: + +1. In addition to the `ACTOR_TEST_PAY_PER_EVENT` environment variable, pass `ACTOR_USE_CHARGING_LOG_DATASET`: + + ```shell + ACTOR_TEST_PAY_PER_EVENT=true ACTOR_USE_CHARGING_LOG_DATASET=true npm start + ``` + +1. Open the `charging_log` dataset. By default, it's stored in the `storage/datasets/charging_log/` directory. The log contains all the events charged throughout the run. Because pricing configuration is stored by the Apify platform, all events have a default price of $1.