docs: add PPE event charging examples and restructure best practices#2263
docs: add PPE event charging examples and restructure best practices#2263
Conversation
marcel-rbro
left a comment
There was a problem hiding this comment.
Some questions and suggestions, overall looks good.
| ### Charge for multiple event types | ||
|
|
||
| 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. | ||
| Charge multiple event types in a single operation. Each event type must be defined in your Actor's pricing configuration. |
There was a problem hiding this comment.
Maybe explain this more thoroughly? The text says "charge multiple event types in a single operation", but the example shows two charge calls. Maybe rewrite to something like: "Charge for multiple different event types in one Actor run"?
| The Apify SDK's `ChargeResult` respects the user set limit already. | ||
| 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). The Apify SDK's `ChargeResult` respects this limit automatically. | ||
|
|
||
| When using [Crawlee](https://crawlee.dev/), use `crawler.autoscaledPool.abort()` instead of `Actor.exit()` to gracefully finish the crawler. |
There was a problem hiding this comment.
Is this the correct place for the Crawlee note? The admonition title doesn't fit with this.
There was a problem hiding this comment.
Yeah I was trying to avoid two admonitions next to each other and may have went overboard with streamlining. Maybe this will be better as regular prose 🤔
| if (chargeResult.eventChargeLimitReached) { | ||
| await Actor.exit(); | ||
| await Actor.exit(); | ||
| } |
There was a problem hiding this comment.
This is the only example that shows this method. Should it be added to the 2 other examples below?
There was a problem hiding this comment.
On one hand it could be, on the other it would make all of those repetitive. My goal here was to create generic code examples, that span few use-cases. Kind of in a way to teach, but not provide production-ready code.
| ### Charge for multiple items at once | ||
|
|
||
| #### Synthetic start event for existing Actors | ||
| Use the `count` parameter to charge for a batch of items in a single call. |
There was a problem hiding this comment.
How to handle eventChargeLimitReached in this case? Or is that outside of the scope of this task? 😆
|
|
||
| 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. | ||
| Charge multiple event types in a single operation. Each event type must be defined in your Actor's pricing configuration. | ||
|
|
There was a problem hiding this comment.
If you have multiple events, use the
chargeableWithinLimitproperty to see if other events can still be charged before stopping the Actor.
But it's not demonstrated anywhere?
There was a problem hiding this comment.
Yeah my starting point was more complex code example and then I started simplifying it, might be the best to remove mention from prose
- Fix "single operation" wording to "in one Actor run" - Move Crawlee note out of ACTOR_MAX_TOTAL_CHARGE_USD admonition into prose - Removed chargeableWithinLimit mention from prose - Add chargedCount handling to batch charging example Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| const data = { url: 'https://example.com', title: 'Example' }; | ||
| const chargeResult = await Actor.charge({ eventName: 'result' }); | ||
|
|
||
| if (chargeResult.eventChargeLimitReached) { | ||
| await Actor.exit(); | ||
| await Actor.exit(); | ||
| } | ||
|
|
||
| // Rest of the Actor logic | ||
| await Actor.pushData(data); |
There was a problem hiding this comment.
Just from a coding standards perspective, it is recommended to keep variables as close as possible to where they are first used. So we could initialize the data right before pushing it 👀
| const data = { url: 'https://example.com', title: 'Example' }; | |
| const chargeResult = await Actor.charge({ eventName: 'result' }); | |
| if (chargeResult.eventChargeLimitReached) { | |
| await Actor.exit(); | |
| await Actor.exit(); | |
| } | |
| // Rest of the Actor logic | |
| await Actor.pushData(data); | |
| const chargeResult = await Actor.charge({ eventName: 'result' }); | |
| if (chargeResult.eventChargeLimitReached) { | |
| await Actor.exit(); | |
| } | |
| const data = { url: 'https://example.com', title: 'Example' }; | |
| await Actor.pushData(data); |
(this applies to the other examples as well)
Add three generic Actor.charge() examples (JS + Python) to the PPE docs: - Charge per result with spending limit check - Charge for multiple event types - Charge for multiple items at once using count parameter Move "Respect user spending limits" from standalone section into best practices and reorder subsections for logical flow: setup, spending limits, charging patterns, then pricing guidance. Clean up best practices intro prose. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix "single operation" wording to "in one Actor run" - Move Crawlee note out of ACTOR_MAX_TOTAL_CHARGE_USD admonition into prose - Removed chargeableWithinLimit mention from prose - Add chargedCount handling to batch charging example Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4930479 to
00ecad4
Compare
|
Preview for this PR was built for commit |
Separates implementation patterns from best practices to support two distinct reader modes: evaluators reading principles vs. implementers looking for working code. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Adds a 'Respect spending limits' code example showing eventChargeLimitReached in a loop and chargeableWithinLimit for multi-event Actors. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Reorders examples simple-to-complex: basic charging patterns first, limit handling last as the advanced safety wrapper. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
00ecad4 to
60efc65
Compare
|
Preview for this PR was built for commit |
60efc65 to
00ecad4
Compare
|
Preview for this PR was built for commit |
|
Preview for this PR was built for commit |
| # chargeable_within_limit lists event types the user's budget still covers | ||
| if 'result' not in charge_result.chargeable_within_limit: | ||
| await Actor.exit('Spending limit reached') |
There was a problem hiding this comment.
I believe the charge_result contains how many events of each type can still be charged. Meaning all registered event types are always present as keys with a value of 0 when exhausted. So this check won't work because the key is always there.
It might probably look something like this:
| # chargeable_within_limit lists event types the user's budget still covers | |
| if 'result' not in charge_result.chargeable_within_limit: | |
| await Actor.exit('Spending limit reached') | |
| # chargeable_within_limit maps event names to remaining counts (None means unlimited) | |
| remaining = charge_result.chargeable_within_limit.get('result', 0) | |
| if remaining is not None and remaining <= 0: | |
| await Actor.exit('Spending limit reached') |
The other examples look good to me. However, @janbuchar has better insight into the PPE implementation. Honzo, could you please double-check the examples? Also, there might be a similar issue like this in the JS version as well.
Add three generic
Actor.charge()examples (JS + Python) to the PPE docs:Move "Respect user spending limits" from standalone section into best practices and reorder subsections for logical flow: setup, spending limits, charging patterns, then pricing guidance. Clean up best practices intro prose.