# Firecrawl on n8n: picking the right operation, and what async does to your workflow

Scrape, crawl, map, search, extract and batch scrape all return web content. Picking wrong costs you an order of magnitude in time or credits. Here is the decision, and the async behaviour that changes how you wire the workflow.

Published: 2026-08-22 · Language: en · Tags: n8n, Firecrawl, Web Scraping, Integration · Canonical: https://hecigo.com/blog/n8n-firecrawl-node-choosing-the-right-operation/

---

`n8n-nodes-firecrawl-v2` exposes ten operations. Six of them return web content, and from the parameter list they look interchangeable. They are not: picking the wrong one costs an order of magnitude in either time or credits, and two of them change the shape of your workflow because they run asynchronously.

The [README](https://github.com/hecigo/n8n-nodes-firecrawl-v2) documents every parameter. This is the part that comes before that: which one you want, and what to expect once it runs.

## The decision in one table

| You know | You want | Operation |
|---|---|---|
| One exact URL | Its content | **Scrape** |
| A list of exact URLs | All their content | **Batch Scrape** |
| A site, not which pages | The URL list, cheaply | **Map** |
| A site, and you want everything | Content of every page under it | **Crawl** |
| Neither the site nor the URL | Pages matching a query | **Search** |
| A page and a schema | Structured fields, not prose | **Extract** |

The two that get misused are Crawl and Extract.

**Crawl** is for when you genuinely want a whole site section. It follows links, respects `includePaths` and `excludePaths`, and it is the expensive one. If you already know which pages you want, Batch Scrape does the same work without the discovery cost.

**Extract** is not a scrape with extra steps. It runs an AI pass over the fetched content against a prompt and a schema, and returns fields rather than markdown. When you want "the price, the SKU and the stock status from each of these product pages", Extract is the operation. When you want the page text and you will parse it yourself, Scrape is cheaper and deterministic.

## Map first is almost always right

The instinct with a site you do not know is to Crawl it and see what comes back. That is the expensive way to find out you only needed nine pages.

Map returns the URL list without fetching content. It is fast and cheap, and it turns an unbounded job into a bounded one:

```text
Firecrawl: Map (https://example.com/docs)
  -> Code: filter the URLs you actually want
  -> Firecrawl: Batch Scrape (the filtered list)
```

Three benefits over Crawl. You see the size before you commit. You filter with real code rather than path-pattern guessing. And the result is reproducible: the same URL list yields the same batch, which matters when you diff this week against last week.

Use Crawl when the site is small enough that enumeration is pointless, or when you genuinely want everything under a path and do not care about the count.

## Async operations change the workflow shape

Crawl, Extract and Batch Scrape are asynchronous. By default they return a **job ID**, not results. Your next node receives an ID and nothing else, which looks like the node failed.

Two ways to handle it, and they suit different jobs:

**Inline.** Set `waitForCompletion` and the node polls every 2 seconds and returns results directly. Simple, and the workflow reads top to bottom. The cost is that the execution occupies a worker slot for the entire duration, which for a large crawl can be minutes.

**Deferred.** Take the job ID, and use Get Crawl Status, Get Extract Status or Get Batch Scrape Status later, either on a schedule or after a Wait node. More nodes, but a long job stops holding a worker hostage.

For anything short and interactive, take the inline path. For a nightly crawl of a large site, take the deferred path so a slow job cannot stall the rest of your queue.

## Three defaults that surprise people

**`waitForCompletion` is not consistent across operations.** Extract defaults to `true`; Crawl and Batch Scrape default to `false`. So the same setting produces inline results in one node and a bare job ID in the next. Set it explicitly on every async operation rather than relying on the default, if only so the workflow reads unambiguously six months later.

**Format support differs by operation.** Scrape supports ten formats, including `json`, `summary`, `audio` and `changeTracking`. Crawl, Search and Batch Scrape support five basic ones. If your workflow depends on `changeTracking` for content monitoring, that constrains you to Scrape, and that is worth knowing before you design around Crawl.

**`onlyMainContent` defaults to `true`.** It strips headers, navigation and footers, which is what you want for an article and wrong for a page whose value is in the nav. Turn it off deliberately rather than wondering where half the page went.

One more that is not a default but bites the same way: `waitFor` is `0` by default. For a server-rendered page that is correct. For a single-page app, the fetch completes before the content exists and you get an empty shell. Raise it and the page appears; the number is per site and you find it by trying.

## Self-hosted versus cloud

The node works against both. The credential difference is one field:

| | Base URL |
|---|---|
| Cloud | `https://api.firecrawl.dev/v2` |
| Self-hosted | `http://your-host:3002/v2` |

**The `/v2` suffix is required in both cases.** Leaving it off is the single most common credential failure, and the error it produces points at authentication rather than at the path, which sends people looking in the wrong place.

Self-hosting is worth it when volume is steady enough that per-request pricing stops making sense, when the pages you fetch must not leave your own infrastructure, or when you want latency measured in your own network rather than across the internet. It costs you an instance to run and patch. Cloud is worth it when volume is bursty or when nobody on the team wants to own another service.

The node does not care which you pick, and switching later is a credential change rather than a workflow change.

## Error handling

The node supports n8n's `continueOnFail`. On failure the item becomes `{ "error": "message" }` and the workflow keeps going.

That is the right default for batch work, where one dead URL out of forty should not kill the run. It is also a trap: **an item with an `error` key still flows downstream and still looks like an item.** Filter for it explicitly before anything writes to a database, or you will store rows whose content field contains an error string.

> Related: [Thu Thập Dữ Liệu Web cho Doanh Nghiệp Việt với n8n và Firecrawl](https://hecigo.com/blog/toi-uu-hoa-quy-trinh-thu-thap-du-lieu-web-cho-doanh-nghiep-viet-voi-n8n-va-firec/): Node Firecrawl trong n8n có sáu thao tác dễ nhầm nhau: scrape, crawl, map, search, get status và cancel. Bài này đi qua từng cái và khi nào dùng...

## The part after the fetch

Getting the content is the first half. The second half is putting it somewhere useful without duplicating it, losing it, or failing silently when a fetch returns nothing.

That second half is what we build and run.

> **Found this useful?** Follow hecigo on [Zalo OA](https://zalo.me/3108963776852260798) for new technical writing, or [get in touch](https://hecigo.com/#contact) if two of your systems need to talk to each other and something is going wrong in between.

> Related: [Read next: Running a Zalo bot on n8n: what breaks between test mode and production](https://hecigo.com/blog/n8n-zalo-bot-node-what-breaks-in-production/): The node works on the first try in test mode. Then you activate the workflow and nothing arrives. Five failure modes we hit running Zalo Bot...

## References

- [n8n-nodes-firecrawl-v2](https://github.com/hecigo/n8n-nodes-firecrawl-v2) - hecigo
- [Firecrawl](https://firecrawl.dev) - Firecrawl
- [Error handling | n8n Docs](https://docs.n8n.io/flow-logic/error-handling/) - n8n

---

Published by hecigo, middleware & integration lab. https://hecigo.com · hi@hecigo.com
