Skip to content

Commit 9651b98

Browse files
docs: Improve PPE docs based on feedback
1 parent e369100 commit 9651b98

File tree

1 file changed

+90
-103
lines changed

1 file changed

+90
-103
lines changed

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

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

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

62135
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) => {
128201
const response = await fetch(url);
129202

130203
if (response.status === 404) {
131-
// Charge for the work done (opening the page)
132-
await Actor.charge({
133-
eventName: "scraped-result",
134-
});
135-
136-
// Return error item instead of failing
204+
// Charge for the work done and return error item in one call
137205
await Actor.pushData({
138206
url: url,
139207
error: "404",
140208
errorMessage: "Page not found"
141-
});
209+
}, 'scraped-result');
142210

143211
return;
144212
}
145213

146-
// Rest of the Actor logic
214+
// Rest of the process_url function
147215
};
148216

149217
await Actor.init();
150218

151-
const main = async () => {
152-
const input = await Actor.getInput();
153-
const { urls } = input;
219+
const input = await Actor.getInput();
220+
const { urls } = input;
154221

155-
for (const url of urls) {
156-
await processUrl(url);
157-
}
158-
159-
// Rest of the Actor logic
160-
};
222+
for (const url of urls) {
223+
await processUrl(url);
224+
}
161225

162-
await main();
226+
// Rest of the Actor logic
163227

164228
await Actor.exit();
165229
```
@@ -175,19 +239,16 @@ async def process_url(url):
175239
response = requests.get(url)
176240

177241
if response.status_code == 404:
178-
# Charge for the work done (opening the page)
179-
await Actor.charge(event_name='scraped-result')
180-
181-
# Return error item instead of failing
182-
await Actor.push_data({
183-
'url': url,
184-
'error': '404',
185-
'errorMessage': 'Page not found'
186-
})
242+
# Charge for the work done and return error item in one call
243+
await Actor.push_data({
244+
'url': url,
245+
'error': '404',
246+
'errorMessage': 'Page not found'
247+
}, 'scraped-result')
187248

188-
return
249+
return
189250

190-
# Rest of the Actor logic
251+
# Rest of the process_url function
191252

192253
async def main():
193254
await Actor.init()
@@ -206,80 +267,6 @@ async def main():
206267
</TabItem>
207268
</Tabs>
208269

209-
### Respect user spending limits
210-
211-
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.
212-
213-
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.
214-
215-
<Tabs groupId="main">
216-
<TabItem value="JavaScript" label="JavaScript">
217-
218-
```js
219-
import { Actor } from 'apify';
220-
221-
const chargForApiProductDetail = async () => {
222-
const chargeResult = await Actor.charge({
223-
eventName: "product-detail",
224-
});
225-
226-
return chargeResult;
227-
};
228-
229-
await Actor.init();
230-
231-
const main = async () => {
232-
// API call, or any other logic that you want to charge for
233-
234-
const chargeResult = await chargForApiProductDetail();
235-
236-
if (chargeResult.eventChargeLimitReached) {
237-
await Actor.exit();
238-
}
239-
240-
// Rest of the Actor logic
241-
};
242-
243-
await main();
244-
245-
await Actor.exit();
246-
```
247-
248-
</TabItem>
249-
<TabItem value="Python" label="Python">
250-
251-
```py
252-
from apify import Actor
253-
254-
async def charge_for_api_product_detail():
255-
charge_result = await Actor.charge(event_name='product-detail')
256-
257-
return charge_result
258-
259-
async def main():
260-
await Actor.init()
261-
262-
# API call, or any other logic that you want to charge for
263-
264-
charge_result = await charge_for_api_product_detail()
265-
266-
if charge_result.event_charge_limit_reached:
267-
await Actor.exit()
268-
269-
# Rest of the Actor logic
270-
271-
await Actor.exit()
272-
```
273-
274-
</TabItem>
275-
</Tabs>
276-
277-
:::note Crawlee integration and spending limits
278-
279-
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.
280-
281-
:::
282-
283270
### Keep pricing simple with fewer events
284271

285272
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)