From b1dc387e23fcd00c1480f82b647e194c73e9debe Mon Sep 17 00:00:00 2001 From: patrikbraborec Date: Thu, 18 Sep 2025 14:54:16 +0200 Subject: [PATCH] docs: Improve PPE docs based on feedback --- .../publishing/monetize/pay_per_event.mdx | 193 ++++++++---------- 1 file changed, 90 insertions(+), 103 deletions(-) diff --git a/sources/platform/actors/publishing/monetize/pay_per_event.mdx b/sources/platform/actors/publishing/monetize/pay_per_event.mdx index 8f238d62bb..fabacead53 100644 --- a/sources/platform/actors/publishing/monetize/pay_per_event.mdx +++ b/sources/platform/actors/publishing/monetize/pay_per_event.mdx @@ -57,6 +57,79 @@ An Actor's negative net profit does not affect the positive profit of another Ac 1. _Test your pricing_: Run your Actor and analyze cost-effectiveness using a special dataset. 1. _Communicate value_: Ensure pricing reflects the value provided and is competitive. +## Respect user spending limits + +Finish the Actor run once charging reaches user-configured maximum cost per run. Apify SDKs (JS and Python) return `ChargeResult` that helps determine when to finish. + +The `eventChargeLimitReached` property checks if the current event type can be charged more. If you have multiple event types, analyze the `chargeableWithinLimit` property to see if other events can still be charged before stopping the Actor. + +:::info ACTOR_MAX_TOTAL_CHARGE_USD environment variable + +For pay-per-event Actors, users set a spending limit through the Apify Console. This limit is available in your Actor code as the `ACTOR_MAX_TOTAL_CHARGE_USD` [environment variable](/platform/actors/development/programming-interface/environment-variables), which contains the user's maximum cost. + +::: + + + + +```js +import { Actor } from 'apify'; + +const chargeForApiProductDetail = async () => { + const chargeResult = await Actor.charge({ eventName: "product-detail" }); + + return chargeResult; +}; + +await Actor.init(); + +// API call, or any other logic that you want to charge for +const chargeResult = await chargeForApiProductDetail(); + +if (chargeResult.eventChargeLimitReached) { + await Actor.exit(); +} + +// Rest of the Actor logic + +await Actor.exit(); +``` + + + + +```py +from apify import Actor + +async def charge_for_api_product_detail(): + charge_result = await Actor.charge(event_name='product-detail') + + return charge_result + +async def main(): + await Actor.init() + + # API call, or any other logic that you want to charge for + + charge_result = await charge_for_api_product_detail() + + if charge_result.event_charge_limit_reached: + await Actor.exit() + + # Rest of the Actor logic + + await Actor.exit() +``` + + + + +:::note Crawlee integration and spending limits + +When using [Crawlee](https://crawlee.dev/), use `crawler.autoscaledPool.abort()` instead of `Actor.exit()` to gracefully finish the crawler and allow the rest of your code to process normally. + +::: + ## Best practices for PPE Actors Use our SDKs ([JS](/sdk/js/) and, [Python](/sdk/python/) or use [`apify actor charge`](/cli/docs/next/reference#apify-actor-charge-eventname) when using our Apify CLI) to simplify PPE implementation into your Actor. This tool can handle pricing, usage tracking, idempotency keys, API errors, and, event charging via an API. @@ -128,38 +201,29 @@ const processUrl = async (url) => { const response = await fetch(url); if (response.status === 404) { - // Charge for the work done (opening the page) - await Actor.charge({ - eventName: "scraped-result", - }); - - // Return error item instead of failing + // Charge for the work done and return error item in one call await Actor.pushData({ url: url, error: "404", errorMessage: "Page not found" - }); + }, 'scraped-result'); return; } - // Rest of the Actor logic + // Rest of the process_url function }; await Actor.init(); -const main = async () => { - const input = await Actor.getInput(); - const { urls } = input; +const input = await Actor.getInput(); +const { urls } = input; - for (const url of urls) { - await processUrl(url); - } - - // Rest of the Actor logic -}; +for (const url of urls) { + await processUrl(url); +} -await main(); +// Rest of the Actor logic await Actor.exit(); ``` @@ -175,19 +239,16 @@ async def process_url(url): response = requests.get(url) if response.status_code == 404: - # Charge for the work done (opening the page) - await Actor.charge(event_name='scraped-result') - - # Return error item instead of failing - await Actor.push_data({ - 'url': url, - 'error': '404', - 'errorMessage': 'Page not found' - }) + # Charge for the work done and return error item in one call + await Actor.push_data({ + 'url': url, + 'error': '404', + 'errorMessage': 'Page not found' + }, 'scraped-result') - return + return - # Rest of the Actor logic + # Rest of the process_url function async def main(): await Actor.init() @@ -206,80 +267,6 @@ async def main(): -### Respect user spending limits - -Finish the Actor run once charging reaches user-configured maximum cost per run. Apify SDKs (JS and Python) return `ChargeResult` that helps determine when to finish. - -The `eventChargeLimitReached` property checks if the current event type can be charged more. If you have multiple event types, analyze the `chargeableWithinLimit` property to see if other events can still be charged before stopping the Actor. - - - - -```js -import { Actor } from 'apify'; - -const chargForApiProductDetail = async () => { - const chargeResult = await Actor.charge({ - eventName: "product-detail", - }); - - return chargeResult; -}; - -await Actor.init(); - -const main = async () => { - // API call, or any other logic that you want to charge for - - const chargeResult = await chargForApiProductDetail(); - - if (chargeResult.eventChargeLimitReached) { - await Actor.exit(); - } - - // Rest of the Actor logic -}; - -await main(); - -await Actor.exit(); -``` - - - - -```py -from apify import Actor - -async def charge_for_api_product_detail(): - charge_result = await Actor.charge(event_name='product-detail') - - return charge_result - -async def main(): - await Actor.init() - - # API call, or any other logic that you want to charge for - - charge_result = await charge_for_api_product_detail() - - if charge_result.event_charge_limit_reached: - await Actor.exit() - - # Rest of the Actor logic - - await Actor.exit() -``` - - - - -:::note Crawlee integration and spending limits - -When using [Crawlee](https://crawlee.dev/), use `crawler.autoscaledPool.abort()` instead of `Actor.exit()` to gracefully finish the crawler and allow the rest of your code to process normally. - -::: - ### Keep pricing simple with fewer events Try to limit the number of events. Fewer events make it easier for users to understand your pricing and predict their costs.