Skip to main content

Apify Memory and CPU Optimization: Cut Actor Costs by 50%

· 6 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

Apify bills by Compute Units: Memory (GB) × runtime (hours). Oversizing Actors wastes money; undersizing causes OOM crashes. This guide covers memory sizing, Cheerio vs Playwright trade-offs, Playwright resource blocking, concurrency tuning, and dataset batching so you cut costs by 30–50% without losing throughput. Optimize your Actors in the Apify Console.

How Apify billing works

Compute Units (CUs) = Memory (GB) × Runtime (hours). Example: 1 GB for 2 hours = 2 CUs. Apify rounds up. A 2 GB Actor that runs 15 minutes still bills for 2 GB × 0.25 h = 0.5 CUs.

Free plan: $5/month in credits (~$0.25 per CU). Paid plans: usage-based. Every MB and minute counts.

Choosing the right memory

Use CaseRecommended MemoryRationale
CheerioCrawler256 MBLightweight, no browser. Rarely needs more.
PlaywrightCrawler (simple)512 MB – 1 GBSingle page, minimal JS. Works for most SERP/product scrapes.
PlaywrightCrawler (heavy)1–2 GBMultiple tabs, complex DOM, PDF generation.
Puppeteer with PDF2 GB+Chromium + PDF rendering is memory-hungry.

Start low. Check Run → Details for peak memory. If you never hit 50% of allocation, downgrade. See the Actor building guide for scaffolding.

CheerioCrawler vs PlaywrightCrawler: CPU and memory

AttributeCheerioCrawlerPlaywrightCrawlerBest For
Memory~50–150 MB300 MB – 2 GBCheerio: static HTML
CPULow (no JS engine)High (full Chromium)Cheerio: bulk scraping
Use caseStatic HTML, APIsJS-rendered, SPAs, screenshotsPlaywright: dynamic content
Concurrency20–50 typical5–15 typicalCheerio: higher throughput
Cost per 1K pages~$0.05–0.10~$0.20–0.50Cheerio: 3–5× cheaper

Winner: Cheerio when HTML is static or pre-fetched. Playwright only when you need rendering, clicks, or screenshots. Use Apify's Website Content Crawler or similar for many sites that load content via API — it often uses Cheerio under the hood.

Disabling images, CSS, and fonts in Playwright

Loading images and styles increases memory and network. Block them when you only need text or structure.

import { PlaywrightCrawler } from 'crawlee';

const crawler = new PlaywrightCrawler({
launchContext: {
launchOptions: {
args: ['--disable-extensions', '--no-sandbox'],
},
},
preNavigationHooks: [
async ({ page }) => {
await page.route('**/*', (route) => {
const type = route.request().resourceType();
if (['image', 'stylesheet', 'font', 'media'].includes(type)) {
route.abort();
} else {
route.continue();
}
});
},
],
async requestHandler({ page, request }) {
// ... extraction logic
},
});

Saves 30–50% memory on image-heavy pages. Test that your selectors still work — some sites inject content via CSS. You can also disable JavaScript entirely for static pages — add await page.setJavaScriptEnabled(false) in a preNavigationHook — but only if the page doesn't need JS to render content.

Request concurrency tuning

maxConcurrency caps parallel requests. minConcurrency controls Crawlee's AutoscaledPool floor. Higher concurrency = faster but more memory and CPU.

ScenariomaxConcurrencyminConcurrencyNotes
Cheerio, cheap targets20–305Throughput-first
Playwright, simple pages10–153Balanced
Playwright, heavy pages5–82Avoid OOM
Rate-limited sites2–51Reduce bans
const crawler = new PlaywrightCrawler({
maxConcurrency: 10,
minConcurrency: 3,
// ...
});

Tune based on Run details. If CPU stays below 50%, increase maxConcurrency. If memory spikes, decrease it.

Autoscaling: when to override

Apify's default autoscaler adjusts concurrency based on system load. For predictable workloads, you can disable it and fix concurrency:

const crawler = new PlaywrightCrawler({
autoscaledPoolOptions: {
autoscale: false, // Fixed concurrency
},
maxConcurrency: 8,
// ...
});

Use fixed concurrency when the default autoscaler over-provisions and wastes CUs. Use autoscale when load varies widely.

Dataset and KV store write batching

Frequent pushData calls add I/O overhead. Crawlee batches internally, but avoid push-per-element in hot loops when possible.

// Better: batch in memory, push periodically
const batch: Record<string, unknown>[] = [];
for (const item of items) {
batch.push(item);
if (batch.length >= 100) {
await Actor.pushData(batch);
batch.length = 0;
}
}
if (batch.length > 0) await Actor.pushData(batch);

For very large runs, consider writing to a KV store in chunks and exporting at the end. Reduces dataset API calls.

Monitoring memory in Console

Run → Details shows peak memory and CPU. Compare to your Actor's allocation. If peak is 400 MB and you're on 2 GB, downgrade to 512 MB and re-run. Saves 75% on that dimension.

Complete optimization checklist

  • Memory set to minimum viable (256 MB Cheerio, 512 MB–1 GB Playwright)
  • Images/CSS/fonts blocked in Playwright where possible
  • maxConcurrency tuned from Run details
  • Cheerio used instead of Playwright where HTML is static
  • Dataset writes batched for high-volume runs

Crawler type comparison

CrawlerMemoryCPUCost/1K pagesBest For
CheerioCrawler256 MBLow$Static HTML, APIs
PlaywrightCrawler (no images)512 MBMedium$$JS pages, light rendering
PlaywrightCrawler (full)1–2 GBHigh$$$Screenshots, PDFs, complex SPAs

Best for cost: Cheerio. Best for fidelity: Playwright with resource blocking. See Actor testing guide to validate before scaling.

Apify Affiliate Banner 728x90Apify Affiliate Banner 728x90Apify Affiliate Banner 300x50Apify Affiliate Banner 300x50
Start with the cheapest option

Default to Cheerio. Switch to Playwright only when pages need rendering. You'll often cut costs by 50% or more.



Optimize your Actors on Apify | Browse the Store

Frequently Asked Questions

Apify bills by Compute Units (CUs) = Memory (GB) × Runtime (hours). A 1 GB Actor running 2 hours costs 2 CUs. Free plan gives ~$5/month; paid plans are usage-based.

Cheerio typically needs 256 MB. Playwright needs 512 MB–2 GB depending on page complexity. Disable images and CSS to reduce Playwright memory by 30–50%.

Use Cheerio for static HTML or when the target serves content without JS. Use Playwright when you need to render JavaScript, take screenshots, or interact with the page. Cheerio is 3–5× cheaper.

Block images, stylesheets, fonts, and media via page.route(). Use --disable-extensions and --no-sandbox in launch args. Reduce maxConcurrency if memory spikes.

Open Run details in Apify Console. The metrics section shows peak memory and CPU. Compare to your Actor's allocation — if usage is low, downsize to save cost.

Batching reduces I/O overhead and can slightly lower runtime. The main savings come from memory sizing and crawler choice. Still, batch when pushing thousands of items.

Common mistakes and fixes

Actor runs out of memory

Increase memory in Actor settings. For Playwright, use 1–2 GB. Disable images and unnecessary resources.

Runs cost more than expected

Check Run details for actual memory/CPU usage. Downsize if usage is consistently below allocation.