'Optimize Firecrawl scraping performance with caching, batch scraping,
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill firecrawl-performance-tuning --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Firecrawl Performance Tuning?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-firecrawl-performance-tuning-643c3068)More formats (shields.io, HTML) on the badges page.
---
name: firecrawl-performance-tuning
description: 'Optimize Firecrawl scraping performance with caching, batch scraping,
and format selection.
Use when experiencing slow scrapes, optimizing credit usage per page,
or building high-throughput scraping pipelines.
Trigger with phrases like "firecrawl performance", "optimize firecrawl",
"firecrawl latency", "firecrawl caching", "firecrawl slow", "firecrawl batch".
'
allowed-tools: Read, Write, Edit
version: 1.11.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- firecrawl
- api
- performance
compatibility: Designed for Claude Code
---
# Firecrawl Performance Tuning
## Overview
Optimize Firecrawl API performance by choosing efficient scraping modes, caching results, using batch endpoints, and minimizing unnecessary rendering. Key levers: format selection (markdown vs HTML vs screenshot), `waitFor` tuning, `onlyMainContent`, and batch vs individual scraping.
## Prerequisites
- A redacted baseline for latency, success rate, queue age, and budget consumption.
- Explicit freshness, retention, target-policy, and capacity requirements for each workflow.
- Synthetic or approved staging targets and a rollback switch for concurrency or cache changes.
## Output
Produce a performance receipt with baseline and post-change aggregate metrics, selected format, concurrency/cache settings, target policy version, owner, and rollback result. Exclude scraped content and credentials.
## Latency Benchmarks
| Operation | Typical | With JS Wait | With Screenshot |
|-----------|---------|--------------|-----------------|
| scrapeUrl (markdown) | 2-5s | 5-10s | 8-15s |
| scrapeUrl (extract) | 3-8s | 8-15s | N/A |
| crawlUrl (10 pages) | 20-40s | 40-80s | N/A |
| mapUrl | 1-3s | N/A | N/A |
| batchScrapeUrls (10) | 10-20s | 20-40s | N/A |
## Instructions
### Step 1: Minimize Formats (Biggest Win)
```typescript
import FirecrawlApp from "@mendable/firecrawl-js";
const firecrawl = new FirecrawlApp({
apiKey: process.env.FIRECRAWL_API_KEY!,
});
// SLOW: requesting everything
const slow = await firecrawl.scrapeUrl(url, {
formats: ["markdown", "html", "links", "screenshot"],
// screenshot + full HTML = 3-5x slower
});
// FAST: request only what you need
const fast = await firecrawl.scrapeUrl(url, {
formats: ["markdown"], // markdown only = fastest
onlyMainContent: true, // skip nav/footer/sidebar
});
```
### Step 2: Tune waitFor for JS-Heavy Pages
```typescript
// Default: no JS wait (fastest, works for static sites)
const staticResult = await firecrawl.scrapeUrl("https://docs.example.com", {
formats: ["markdown"],
// No waitFor needed — content is in initial HTML
});
// SPA/dynamic pages: add minimal wait
const spaResult = await firecrawl.scrapeUrl("https://app.example.com", {
formats: ["markdown"],
waitFor: 3000, // 3s — enough for most SPAs
onlyMainContent: true,
});
// Heavy interactive page: use actions instead of long wait
const heavyResult = await firecrawl.scrapeUrl("https://dashboard.example.com", {
formats: ["markdown"],
actions: [
{ type: "wait", selector: ".data-table" }, // wait for specific element
{ type: "scroll", direction: "down" }, // trigger lazy loading
],
});
```
### Step 3: Cache Scraped Content
```typescript
import { LRUCache } from "lru-cache";
import { createHash } from "crypto";
const scrapeCache = new LRUCache<string, any>({
max: 500, // max 500 cached pages
ttl: 3600000, // 1 hour TTL
});
async function cachedScrape(url: string) {
const key = createHash("md5").update(url).digest("hex");
const cached = scrapeCache.get(key);
if (cached) {
console.log(`Cache hit: ${url}`);
return cached;
}
const result = await firecrawl.scrapeUrl(url, {
formats: ["markdown"],
onlyMainContent: true,
});
if (result.success) {
scrapeCache.set(key, result);
}
return result;
}
// Typical savings: 50-80% credit reduction for repeated scrapes
```
### Step 4: Use Batch Scrape for Multiple URLs
```typescript
// SLOW: sequential individual scrapes
const urls = ["https://a.com", "https://b.com", "https://c.com"];
for (const url of urls) {
await firecrawl.scrapeUrl(url, { formats: ["markdown"] }); // 3 API calls
}
// FAST: single batch scrape call
const batchResult = await firecrawl.batchScrapeUrls(urls, {
formats: ["markdown"],
onlyMainContent: true,
});
// 1 API call, internally parallelized
```
### Step 5: Map Before Crawl (Save Credits)
```typescript
// EXPENSIVE: crawl everything, filter later
await firecrawl.crawlUrl("https://docs.example.com", { limit: 1000 });
// CHEAPER: map first (1 credit), then scrape only what you need
const map = await firecrawl.mapUrl("https://docs.example.com");
const apiDocs = (map.links || []).filter(url =>
url.includes("/api/") || url.includes("/reference/")
);
console.log(`Map: ${map.links?.length} total, ${apiDocs.length} relevant`);
// Batch scrape only relevant URLs
const result = await firecrawl.batchScrapeUrls(apiDocs.slice(0, 50), {
formats: ["markdown"],
});
```
### Step 6: Measure Scrape Performance
```typescript
async function timedScrape(url: string) {
const start = Date.now();
const result = await firecrawl.scrapeUrl(url, { formats: ["markdown"] });
const duration = Date.now() - start;
console.log({
url,
durationMs: duration,
contentLength: result.markdown?.length || 0,
success: result.success,
charsPerSecond: Math.round((result.markdown?.length || 0) / (duration / 1000)),
});
return result;
}
```
## Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| Scrape > 10s | Screenshot or full HTML requested | Use `formats: ["markdown"]` only |
| Empty content | `waitFor` too short for SPA | Increase or use `actions` with selector |
| High credit burn | Scraping same URLs repeatedly | Implement URL-based caching |
| Batch timeout | Too many URLs in one batch | Split into chunks of 50 |
| Cache stale data | TTL too long | Reduce TTL or add cache invalidation |
## Examples
### Performance Comparison Script
```typescript
const url = "https://docs.firecrawl.dev";
// Compare different format configurations
for (const formats of [["markdown"], ["markdown", "html"], ["markdown", "html", "screenshot"]]) {
const start = Date.now();
await firecrawl.scrapeUrl(url, { formats: formats as any, onlyMainContent: true });
console.log(`${formats.join(",")}: ${Date.now() - start}ms`);
}
```
## Resources
- [Advanced Scraping Guide](https://docs.firecrawl.dev/advanced-scraping-guide)
- [Batch Scrape](https://docs.firecrawl.dev/features/batch-scrape)
- [Map Endpoint](https://docs.firecrawl.dev/features/map)
- [LRU Cache](https://github.com/isaacs/node-lru-cache)
## Next Steps
For cost optimization, see `firecrawl-cost-tuning`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!