You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: sources/platform/actors/publishing/monetize/pay_per_event.mdx
+65-22Lines changed: 65 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -66,11 +66,17 @@ Set memory limits using `minMemoryMbytes` and `maxMemoryMbytes` in your [`actor.
66
66
"actorSpecification": 1,
67
67
"name": "name-of-my-scraper",
68
68
"version": "0.0",
69
-
"minMemoryMbytes": 256,
70
-
"maxMemoryMbytes": 4096,
69
+
"minMemoryMbytes": 512,
70
+
"maxMemoryMbytes": 1024,
71
71
}
72
72
```
73
73
74
+
:::note Memory requirements for browser-based scraping
75
+
76
+
When using browser automation tools like Puppeteer or Playwright for web scraping, increase the memory limits to accommodate the browser's memory usage.
77
+
78
+
:::
79
+
74
80
### Charge for "Actor start"
75
81
76
82
Charge for "Actor start" to prevent users from running your Actor for free.
@@ -134,36 +140,51 @@ async def main():
134
140
:::note Actor migrations and charging
135
141
136
142
Actors can migrate between servers during execution, which restarts the process and clears memory. When using PPE charging, avoid charging the start event multiple times after a migration by checking your charging state.
143
+
137
144
:::
138
145
139
146
### Charge for invalid input
140
147
141
-
Charge for invalid input or empty search to prevent users from running your Actor for free.
148
+
Charge for things like URLs that appear valid but lead to errors (like 404s) since you actually had to open the page to discover the error. Return error items with proper error codes and messages instead of failing the entire Actor run.
142
149
143
150
<TabsgroupId="main">
144
151
<TabItemvalue="JavaScript"label="JavaScript">
145
152
146
153
```js
147
154
import { Actor } from'apify';
148
155
149
-
constparseInput=async (input) => {
150
-
if (!input.searchStringsArray&&!input.startUrls) {
156
+
constprocessUrl=async (url) => {
157
+
constresponse=awaitfetch(url);
158
+
159
+
if (response.status===404) {
160
+
// Charge for the work done (opening the page)
151
161
awaitActor.charge({
152
-
eventName:"invalid-input",
162
+
eventName:"scraped-result",
163
+
});
164
+
165
+
// Return error item instead of failing
166
+
awaitActor.pushData({
167
+
url: url,
168
+
error:"404",
169
+
errorMessage:"Page not found"
153
170
});
154
171
155
-
throwawaitActor.fail('INVALID INPUT: You must provide either search terms, or start URLs!');
raiseawait Actor.fail('INVALID INPUT: You must provide either search terms, or start URLs!')
217
+
return
186
218
187
-
return input_data
219
+
# Rest of the Actor logic
188
220
189
221
asyncdefmain():
190
222
await Actor.init()
191
223
192
224
input_data =await Actor.get_input()
193
-
parsed_input =await parse_input(input_data)
225
+
urls = input_data.get('urls', [])
226
+
227
+
for url in urls:
228
+
await process_url(url)
194
229
195
230
# Rest of the Actor logic
196
-
231
+
197
232
await Actor.exit()
198
233
```
199
234
@@ -204,6 +239,8 @@ async def main():
204
239
205
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.
206
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
+
207
244
<TabsgroupId="main">
208
245
<TabItemvalue="JavaScript"label="JavaScript">
209
246
@@ -266,9 +303,11 @@ async def main():
266
303
</TabItem>
267
304
</Tabs>
268
305
269
-
### Use idempotency keys to prevent double charges
306
+
:::note Crawlee integration and spending limits
270
307
271
-
If you're not using the Apify SDKs (JS/Python), you need to handle idempotency (ensuring the same operation produces the same result when called multiple times) manually to prevent charging the same event multiple times.
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
+
:::
272
311
273
312
### Keep pricing simple with fewer events
274
313
@@ -292,21 +331,25 @@ Avoid charging for:
292
331
293
332
This helps users understand exactly what they're paying for and builds trust in your pricing model.
294
333
334
+
### Use idempotency keys to prevent double charges
335
+
336
+
If you're not using the Apify SDKs (JS/Python), you need to handle idempotency (ensuring the same operation produces the same result when called multiple times) manually to prevent charging the same event multiple times.
337
+
295
338
## Example of a pay-per-event pricing model
296
339
297
340
You make your Actor pay-per-event and set the following pricing:
298
341
299
342
-_"actor-start" event_: $0.10 per start
300
343
-_"scraped-product" event_: $0.01 per product
301
-
-_"api-call" event_: $0.05 per API call
344
+
-_"scraped-product-detail" event_: $0.05 per detail
302
345
303
346
During the first month, three users use your Actor:
304
347
305
-
-_User 1 (paid plan)_: Starts Actor 5 times, scrapes 1,000 products, makes 50 API calls
348
+
-_User 1 (paid plan)_: Starts Actor 5 times, scrapes 1,000 products, makes 50 product details
0 commit comments