Skip to main content

Pay per event pricing model

The pay per event (PPE) pricing model offers a flexible monetization option for Actors on Apify Store. The key benefits are:

  • Unlimited revenue scalability
  • AI/MCP compatibility
  • Predictable user costs
  • Store discounts available
  • Custom event billing
  • Two automatically charged events available

About events

An event is a specific, measurable action that your Actor triggers programmatically and charges the user for. The most common events include:

  • Creating a result.
  • Processing an item.
  • Calling an external API.

You define the events and can set different pricing for each event type and discount tier.

Synthetic events

Synthetic events are predefined by Apify and charged automatically:

You can remove synthetic events or change their price in Apify Console.

Custom events

To charge users for the unique value that your Actor delivers, define custom events. You name each event, describe it, set its price, and charge it from your Actor's code.

Get event names

To retrieve the list of all chargeable event names for your Actor, use the Get Actor API endpoint.

Calculate your profit

Your profit is calculated from the following formula:

profit = (0.8 * revenue) - platform costs

where:

  • revenue is the amount charged for events through the PPE charging API or JS/Python SDK. You receive 80% of this revenue.
  • platform costs are the underlying platform usage costs for running the Actor. For details, see Compute costs for PPE Actors.

Only revenue and cost for Apify customers on paid plans are taken into consideration when computing your profit. Users on free plans aren't reflected there.

Avoid negative profit

To make your Actor profitable, price your events so that the revenue covers the platform costs. For details, see Compute costs for PPE Actors.

If your Actor produces negative profit, make sure to:

When testing your Actor pricing, you can also temporarily pass platform usage costs on to users.

Negative profit isolation

An Actor's negative net profit doesn't affect the positive profit of another Actor. For aggregation purposes, an Actor with a negative net profit is considered to have a profit of $0.

  • Without isolation: Total profit = (-$90) + $100 = $10
  • With isolation: Total profit = $0 + $100 = $100

Set pricing for PPE Actors

  1. Understand your costs: Analyze resource usage (e.g CPU, memory, proxies, external APIs) and identify cost drivers.
  2. Define clear events: break your Actor's functionality into measurable, chargeable events.
  3. Choose a primary event: Pick the event that best represents the main value of your Actor. This primary event is highlighted on the Actor detail page, so users quickly understand what they are mainly paying for.
  4. Common use cases:
    1. For scraping: combine Actor start and dataset items pricing to reflect setup and per-result cost.
    2. Beyond scraping: Account for integrations with external systems or external API calls.
  5. External API costs: Account for additional processing costs.
  6. Test your pricing: Run your Actor and analyze cost-effectiveness using a special dataset.
  7. Communicate value: Ensure pricing reflects the value provided and is competitive.

Pass platform usage costs on to users

To avoid negative profit margins during initial development or when testing pricing strategies, you can pass the platform usage costs on to users. As a result, users pay for both the defined events and the underlying platform usage.

Reduced pricing transparency

Charging platform usage separately is less transparent for users and may discourage them from using your Actor. It also negatively impacts your Actor quality score. Use this approach only until you determine accurate event prices.

To pass platform usage costs on to users:

  1. Go to the Actor pricing step of the monetization setup.
  2. In Additional options, switch on the Pay per event + usage toggle.

Turning this option on takes 14 days to take effect. However, you can turn it off immediately, as it is a positive change for users.

Best practices

To simplify PPE implementation into you Actor, use the Apify SDKs. They help you handle pricing, usage tracking, idempotency keys, API errors, and event charging through an API. Without the SDKs, you must handle API integration and possible edge cases manually.

To implement event charging with the Apify CLI, use the apify actor charge command.

You can also call the Charge events in run API endpoint directly.

Respect user spending limits

Your Actor users can set the maximum cost for the run in Apify Console or with the API. It helps users control their spending, as they won't be billed beyond the limit.

The Apify platform enforces this limit. Once it's reached, Actor.charge() stop charging and Actor.pushData() stops pushing data over the limit. The platform then aborts the run automatically. However, the run keeps consuming platform resources for a short time before it stops. For details, see Handle graceful shutdown.

To protect your profits, finish the Actor run once charges reach the user's limit.

Set a minimum for the user limit

To prevent users from setting the maximum cost for the run too low, set the minimum value during the monetization setup. For example, you can use it to cover the price of starting the Actor and producing one result.

The minimum value is stored by the minimalMaxTotalChargeUsd property.

The user spending limit is available in your Actor code as the ACTOR_MAX_TOTAL_CHARGE_USD environment variable, and ChargeResult already accounts for it. Returning a ChargeResult depends on the event type:

  • For custom events, Actor.charge() and Actor.pushData() called with an event name always return a ChargeResult.
  • For synthetic events, pushing datasets items is charged automatically. In the Python SDK, Actor.pushData() returns a ChargeResult. In the JavaScript SDK, Actor.pushData() called without an event name returns void.

Inspect the ChargeResult properties to decide when to stop the run:

PropertyDescription
eventChargeLimitReachedChecks if the user's limit allows for another charge of the event.
chargeableWithinLimitIndicates how many more of each event you can still charge within the remaining budget. Use it if you have multiple events.
chargedCountIndicates how many events were billed by the call. For batch charges (count), it can be lower than you requested. Charges above chargedCount are not billed.
The limit is per run, not per user

ACTOR_MAX_TOTAL_CHARGE_USD and the ChargeResult limit apply to a single run, not to a user's total spending. If a user starts many runs at once, your combined platform costs across all runs can grow beyond what a single run's limit suggests. To protect your profit, set memory limits and keep per-event platform costs low.

Make sure to:

  • Charge for work as soon as it's done, not in a large batch at the end. If the run stops early, produced results don't get charged.
  • Charge for results after they're saved and available for users. Don't bill users for what they can't access, for example if a run gets aborted.
  • Finish the run once eventChargeLimitReached is true.
Consider using ChargingManager

If charging happens in multiple places across your code, or when you use an Actor where you don't directly control the main loop, you can use the ChargingManager to check the remaining budget periodically instead of inspecting every ChargeResult.

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();
Crawlee integration and spending limits

When using Crawlee, use crawler.autoscaledPool.abort() instead of Actor.exit() to gracefully finish the crawler and let the rest of your code run normally.

Handle graceful shutdown

When a platform or user gracefully aborts a run, the platform sends the aborting event and force-stops the run 30 seconds later. Use this window to bill for work already done, so an abort doesn't cost you the revenue.

Apify SDKs already handle the graceful shutdown for you. They listen for aborting and call Actor.exit() automatically. Register your own aborting handler to:

  • Charge for completed but unbilled work with Actor.charge().
  • Push buffered results with Actor.pushData().
  • Persist state so a resurrected run can resume instead of redoing paid work.
  • Close open resources, such as browsers, database connections, or files.

Your handler runs alongside the SDK's, and Actor.exit() waits for all event listeners to finish before terminating, so your cleanup completes within the 30-second window.

import { Actor } from 'apify';

await Actor.init();

Actor.on('aborting', async () => {
// Charge for completed but unbilled work
});

// Rest of the Actor logic

await Actor.exit();
Non-graceful aborts are default behavior

A non-graceful abort stops the run immediately, without emitting aborting, so your handler never runs. Don't rely on it and charge for work as soon as it's done. For details, see Respect user spending limits.

Use apify-actor-start

The apify-actor-start synthetic event reduces your startup costs and makes your Actor pricing more competitive. When enabled, Apify covers the compute cost of the first 5 seconds of every run.

Synthetic Actor start event recommended

Apify recommends using the synthetic apify-actor-start event in all Actors. It benefits both you and your users.

The apify-actor-start synthetic event works as follows:

  • It's available by default for all new PPE Actors. For existing Actors, you can enable it in Apify Console.
  • Apify automatically charges the event. Don't manually charge the event in your Actor code, as it will fail.
  • The default price of the event is $0.00005, which equals to $0.05 per 1,000 starts. To keep your Actor competitive, Apify recommends keeping the default price.
  • The number of events charged depends on the amount of memory allocated to the run. Up to and including 1 GB of RAM, the event is charged once. Then, it's charged once for each extra GB of memory. For example:
    • 128 MB RAM: 1 event, $0.00005
    • 1 GB RAM: 1 event, $0.00005
    • 4 GB RAM: 4 events, $0.0002
  • You can increase the price of the event, but you won't get more free compute.
  • You can delete the event, but then you will lose the free 5 seconds of compute.

Synthetic start event for new and existing Actors

For new Actors, the apify-actor-start event is added automatically.

For existing Actors, you can add this event manually:

  1. Access your Actor's monetization setup.
  2. In the Actor pricing step, under Event name, add apify-actor-start.
  3. Complete the remaining event details: title, description, and price.

Actors with a custom start event

Don't use both the synthetic and custom start events.

To use the synthetic event, remove the existing custom event from your Actor in Apify Console.

Use apify-default-dataset-item

The apify-default-dataset-item synthetic event lets you align PPE pricing with per-result use cases:

  • It automatically charges users for each item that your Actor writes to the default dataset. For example, when using Actor.pushData().
  • It applies only to the default dataset of the run.
  • It's enabled by default, you don't need to add extra charging code to your Actor.

To stop charging for default dataset items automatically, remove this event from your Actor in Apify Console.

Set memory limits

Memory allocation is the user's choice. By setting an upper limit, you prevent users from selecting more memory than your Actor needs and avoid higher platform costs.

To set memory limits, use the minMemoryMbytes and maxMemoryMbytes properties in your actor.json file:

{
"actorSpecification": 1,
"name": "name-of-my-actor",
"version": "0.0",
"minMemoryMbytes": 512,
"maxMemoryMbytes": 1024,
}
Memory requirements for browser-based scraping

When using browser automation tools like Puppeteer or Playwright for web scraping, increase the memory limits to accommodate the browser's memory usage.

Charge for invalid input

Charge for inputs that seem valid but lead to errors, such as URLs that return a 404. Even though such action doesn't produce usable data, you still spend platform resources opening the page to discover the problem.

Note that charging for invalid input might not cover the entire cost of platform resources spent on handling errors. Test your Actor with invalid input and adjust the validation logic, if necessary.

Instead of failing the entire run because of the bad input, return error items with clear error codes and messages. The run continues for the valid inputs and gives users a transparent record of what they were charged for.

The following example shows how to charge for invalid inputs using Actor.pushData() when a dataset item is created and the scraped-result event is charged:

import { Actor } from 'apify';

const processUrl = async (url) => {
const response = await fetch(url);

if (response.status === 404) {
// 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 process_url function
};

await Actor.init();

const input = await Actor.getInput();
const { urls } = input;

for (const url of urls) {
await processUrl(url);
}

// Rest of the Actor logic

await Actor.exit();

Keep pricing simple

Limit the number of events. Fewer events make it easier for users to understand your pricing and predict the costs.

Avoid cases where producing a single result triggers too many events. It makes your Actor more expensive.

Make events produce visible results

Don't charge users for actions they don't see. For Actors that produce data, events should map to concrete results in the user's dataset or storage.

You can still implement events that don't produce tangible results, such as running AI workflows or processing external API calls. In such cases, inform users about actions performed by your Actor by displaying a status message or pushing a record to the dataset. It helps users understand what actions they were charged for.

The following examples show clear mapping of events to results:

  • post event: Each charge adds one social media post to the dataset.
  • profile event: Each charge adds one user profile to the dataset.
  • processed-image event: Each charge adds one processed image to the dataset.
  • ai-analysis event: Each charge processes one document through an AI workflow.

Use idempotency keys

Apify SDKs handle idempotency for you.

If you're not using the Apify SDKs, implement idempotency to prevent charging for the same event multiple times.

Example of a PPE pricing

You create a social media monitoring Actor with the following pricing:

  • post: $0.002 per post - count every social media post you extract.
  • profile: $0.005 per profile - count every user profile you extract.
  • sentiment-analysis: $0.01 per post - count every post analyzed for sentiment, engagement metrics, and content classification using external LLM APIs.
Fixed pricing vs. usage-based pricing

You have two main strategies for charging AI-related operations:

  1. Fixed event pricing (like sentiment-analysis above): Charge a fixed amount per operation, regardless of actual LLM costs
  2. Usage-based pricing: Use events like llm-token that charge based on actual LLM usage costs

Fixed pricing is simpler for users to predict, while usage-based pricing more accurately reflects your actual costs.

Pricing breakdown by user

UserPlanEventsChargesTotalPlatform cost
1Paid plan
5,000 × post
1,000 × sentiment-analysis
5,000 × $0.002
1,000 × $0.01
$20$2.50
2Paid plan
3,000 × post
500 × sentiment-analysis
3,000 × $0.002
500 × $0.01
$11$1.50
3Free plan
1,000 × post
100 × sentiment-analysis
1,000 × $0.002
100 × $0.01
$3$0.40

Your profit and costs are computed only from the first two users since they are on Apify paid plans.

The platform usage costs are just examples, but you can see the actual costs in the Computing your costs for PPE Actors section.

Revenue breakdown

  • Revenue (paid users only): $20 + $11 = $31
  • Platform cost (paid users only): $2.50 + $1.50 = $4
  • Profit: 0.8 × $31 − $4 = $20.80

Next steps