Skip to content

Commit de33912

Browse files
docs: Improve PPE docs based on feedback
1 parent be24b28 commit de33912

File tree

1 file changed

+92
-109
lines changed

1 file changed

+92
-109
lines changed

sources/platform/actors/publishing/monetize/pay_per_event.mdx

Lines changed: 92 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,79 @@ An Actor's negative net profit does not affect the positive profit of another Ac
5151
1. _Test your pricing_: Run your Actor and analyze cost-effectiveness using a special dataset.
5252
1. _Communicate value_: Ensure pricing reflects the value provided and is competitive.
5353

54+
## Respect user spending limits
55+
56+
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.
57+
58+
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.
59+
60+
:::info ACTOR_MAX_TOTAL_CHARGE_USD environment variable
61+
62+
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.
63+
64+
:::
65+
66+
<Tabs groupId="main">
67+
<TabItem value="JavaScript" label="JavaScript">
68+
69+
```js
70+
import { Actor } from 'apify';
71+
72+
const chargeForApiProductDetail = async () => {
73+
const chargeResult = await Actor.charge({ eventName: "product-detail" });
74+
75+
return chargeResult;
76+
};
77+
78+
await Actor.init();
79+
80+
// API call, or any other logic that you want to charge for
81+
const chargeResult = await chargeForApiProductDetail();
82+
83+
if (chargeResult.eventChargeLimitReached) {
84+
await Actor.exit();
85+
}
86+
87+
// Rest of the Actor logic
88+
89+
await Actor.exit();
90+
```
91+
92+
</TabItem>
93+
<TabItem value="Python" label="Python">
94+
95+
```py
96+
from apify import Actor
97+
98+
async def charge_for_api_product_detail():
99+
charge_result = await Actor.charge(event_name='product-detail')
100+
101+
return charge_result
102+
103+
async def main():
104+
await Actor.init()
105+
106+
# API call, or any other logic that you want to charge for
107+
108+
charge_result = await charge_for_api_product_detail()
109+
110+
if charge_result.event_charge_limit_reached:
111+
await Actor.exit()
112+
113+
# Rest of the Actor logic
114+
115+
await Actor.exit()
116+
```
117+
118+
</TabItem>
119+
</Tabs>
120+
121+
:::note Crawlee integration and spending limits
122+
123+
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.
124+
125+
:::
126+
54127
## Best practices for PPE Actors
55128

56129
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.
@@ -100,13 +173,9 @@ const chargeForActorStart = async () => {
100173

101174
await Actor.init();
102175

103-
const main = async () => {
104-
await chargeForActorStart();
105-
106-
// Rest of the Actor logic
107-
};
176+
await chargeForActorStart();
108177

109-
await main();
178+
// Rest of the Actor logic
110179

111180
await Actor.exit();
112181
```
@@ -157,38 +226,29 @@ const processUrl = async (url) => {
157226
const response = await fetch(url);
158227

159228
if (response.status === 404) {
160-
// Charge for the work done (opening the page)
161-
await Actor.charge({
162-
eventName: "scraped-result",
163-
});
164-
165-
// Return error item instead of failing
229+
// Charge for the work done and return error item in one call
166230
await Actor.pushData({
167231
url: url,
168232
error: "404",
169233
errorMessage: "Page not found"
170-
});
234+
}, 'scraped-result');
171235

172236
return;
173237
}
174238

175-
// Rest of the Actor logic
239+
// Rest of the process_url function
176240
};
177241

178242
await Actor.init();
179243

180-
const main = async () => {
181-
const input = await Actor.getInput();
182-
const { urls } = input;
244+
const input = await Actor.getInput();
245+
const { urls } = input;
183246

184-
for (const url of urls) {
185-
await processUrl(url);
186-
}
187-
188-
// Rest of the Actor logic
189-
};
247+
for (const url of urls) {
248+
await processUrl(url);
249+
}
190250

191-
await main();
251+
// Rest of the Actor logic
192252

193253
await Actor.exit();
194254
```
@@ -204,19 +264,16 @@ async def process_url(url):
204264
response = requests.get(url)
205265

206266
if response.status_code == 404:
207-
# Charge for the work done (opening the page)
208-
await Actor.charge(event_name='scraped-result')
209-
210-
# Return error item instead of failing
211-
await Actor.push_data({
212-
'url': url,
213-
'error': '404',
214-
'errorMessage': 'Page not found'
215-
})
267+
# Charge for the work done and return error item in one call
268+
await Actor.push_data({
269+
'url': url,
270+
'error': '404',
271+
'errorMessage': 'Page not found'
272+
}, 'scraped-result')
216273

217-
return
274+
return
218275

219-
# Rest of the Actor logic
276+
# Rest of the process_url function
220277

221278
async def main():
222279
await Actor.init()
@@ -235,80 +292,6 @@ async def main():
235292
</TabItem>
236293
</Tabs>
237294

238-
### Respect user spending limits
239-
240-
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.
241-
242-
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.
243-
244-
<Tabs groupId="main">
245-
<TabItem value="JavaScript" label="JavaScript">
246-
247-
```js
248-
import { Actor } from 'apify';
249-
250-
const chargForApiProductDetail = async () => {
251-
const chargeResult = await Actor.charge({
252-
eventName: "product-detail",
253-
});
254-
255-
return chargeResult;
256-
};
257-
258-
await Actor.init();
259-
260-
const main = async () => {
261-
// API call, or any other logic that you want to charge for
262-
263-
const chargeResult = await chargForApiProductDetail();
264-
265-
if (chargeResult.eventChargeLimitReached) {
266-
await Actor.exit();
267-
}
268-
269-
// Rest of the Actor logic
270-
};
271-
272-
await main();
273-
274-
await Actor.exit();
275-
```
276-
277-
</TabItem>
278-
<TabItem value="Python" label="Python">
279-
280-
```py
281-
from apify import Actor
282-
283-
async def charge_for_api_product_detail():
284-
charge_result = await Actor.charge(event_name='product-detail')
285-
286-
return charge_result
287-
288-
async def main():
289-
await Actor.init()
290-
291-
# API call, or any other logic that you want to charge for
292-
293-
charge_result = await charge_for_api_product_detail()
294-
295-
if charge_result.event_charge_limit_reached:
296-
await Actor.exit()
297-
298-
# Rest of the Actor logic
299-
300-
await Actor.exit()
301-
```
302-
303-
</TabItem>
304-
</Tabs>
305-
306-
:::note Crawlee integration and spending limits
307-
308-
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.
309-
310-
:::
311-
312295
### Keep pricing simple with fewer events
313296

314297
Try to limit the number of events. Fewer events make it easier for users to understand your pricing and predict their costs.

0 commit comments

Comments
 (0)