Firecrawl CLI Setup: Skip the Installer That Rewrites Your Editors
In the last post I gave the Firecrawl setup three sentences and moved on, because the post was about the fetch skill I was building and not about installation.
The install itself is one command. Everything around the install is what you have to watch: an installer that wants to configure every editor on your machine, an auth screen that reports two contradictory things in consecutive lines, and a shell issue that cost me an API key I could only copy once. Plus one fork in the road worth deciding before you start, which is whether the tool should run as a command or as a resident MCP server.
So here is the setup told properly, for somebody who is installing Firecrawl right now and would rather not spend the time I spent. Everything below was run on this machine against CLI version 1.23.3.
- Install With npm, Not With the Installer
- CLI or MCP: Pick the One That Stays Out of the Way
- The Auth Screen That Contradicts Itself
- The Almost-Right Command
- Prove It Works Before You Trust It
- The Setup Summary
Install With npm, Not With the Installer
The command you want:
npm install -g firecrawl-cli
That is the whole install. You get a firecrawl binary on your PATH, symlinked into your global node modules like any other npm package, and nothing else on your machine changes.
The docs will also offer you a curl-piped-to-shell one-liner, and the CLI ships a firecrawl init command. Both do considerably more than install a binary. Here is init describing itself:
Usage: firecrawl init [options] [template]
Set up Firecrawl: install CLI, authenticate, add integrations, and scaffold a
template
Arguments:
...
Options:
--all Explicitly install skills to all detected agents (default
unless --agent is used)
-y, --yes Run init non-interactively; skills still install globally
across all detected agents unless --agent is used
-g, --global Install skills globally (user-level, default)
-a, --agent <agent> Install skills to a specific agent
-k, --api-key <key> Authenticate with this API key (skips interactive login)
--skip-install Skip global CLI installation
--skip-auth Skip authentication
--skip-skills Skip skills installation
Read the -y line again. Skills still install globally across all detected agents. The non-interactive flag does not mean “do less.” It means “do all of it without asking.”
And init is not alone. There are four separate commands in this CLI whose actual job is writing Firecrawl into tools that are not Firecrawl:

| Command | What it does to your machine |
|---|---|
init | Installs the CLI, authenticates, and pushes agent skills to every detected agent |
setup | Installs skills, workflows, mcp, or defaults individually |
make default | Makes Firecrawl the default provider for supported workflows |
launch | Installs the Firecrawl MCP server into an agent, then starts that agent |
The one that gave me pause is setup, and specifically its undo flag, which is documented as “Undo setup defaults by re-enabling native web tools where supported.” Read that again. The undo re-enables your native web tools, which means the thing being undone disabled them. A scraping vendor’s installer, in its default path, turns off the web fetching your coding agent already had.
I guess that is the reasonable end state of a competitive market where the install experience is a growth channel, and Firecrawl is far from the only tool doing it. It is also the thing I do not want, because I would like to be the one who decides what is in their context.
Skip all four. One npm install does the job.
You can confirm you got the right outcome:
which firecrawl
# /opt/homebrew/bin/firecrawl
firecrawl --version
# 1.23.3
CLI or MCP: Pick the One That Stays Out of the Way
I run Firecrawl through the CLI rather than its MCP server. This is the same verdict I landed on with Playwright.
An MCP server is resident. Its tool definitions sit in the agent’s context on every single request, whether or not this particular request has anything to do with scraping. That is a fixed tax paid in tokens and in attention, on every turn, for a capability I use maybe twice a week.
A CLI is not resident. The agent runs a command, the output goes to a file, and between calls the tool occupies exactly nothing. When I need Firecrawl, a skill tells the agent the command to run. When I do not, there is no evidence in the context that Firecrawl exists.
The launch command exists specifically to install the MCP server into your agent and then start it, and it knows about claude, code/vscode, codex, codex-app, hermes, openclaw, and opencode. That is a well-built feature and I understand why people want it.

Here is the counterargument, because there is one. An MCP server means the agent discovers the capability on its own. It sees a scraping tool in its tool list and reaches for it without being told. With the CLI approach, something has to tell the agent that Firecrawl is an option, which in my case is a skill I had to write. If you do not want to write that glue, the MCP server is genuinely the faster road.
I already had the glue. So for me the CLI wins on the only part I care about, which is that a background tool beats one that takes over the screen.
The Auth Screen That Contradicts Itself
Get your API key from the dashboard, then check your configuration. This is what firecrawl view-config printed for me:
┌─────────────────────────────────────────┐
│ Firecrawl Configuration │
└─────────────────────────────────────────┘
Status: ✓ Authenticated
API Key: Not set
API URL: https://api.firecrawl.dev
Config: /Users/<you>/Library/Application Support/firecrawl-cli
Authenticated, with a check mark. API key not set. Two lines apart, both delivered with total confidence.
I spent real time on this, and the answer turns out to be that neither line is lying. They are answering two different questions but forgot to label which is which.
Status: reflects live authentication state, including the FIRECRAWL_API_KEY environment variable. API Key: reflects only a key stored in the CLI’s own config file, which is empty when you authenticate through the environment instead of through firecrawl config. Two auth paths, two readouts, stacked adjacently with no indication that they are reading different sources.
You can prove it in one command. Clear the environment variable and run the same thing:
env -u FIRECRAWL_API_KEY firecrawl view-config
Status: Not authenticated
Run any command to start authentication, or use:
firecrawl config Authenticate with browser or API key
The status flipped, which means the status was reading the environment variable all along.
The command you actually want is firecrawl --status, which does not have this problem because it names its source:
🔥 firecrawl cli v1.23.3
● Authenticated via FIRECRAWL_API_KEY
Concurrency: 0/2 jobs (parallel scrape limit)
Credits: 1,324 / 1,000 (132% left this cycle)
.firecrawl: not found - no local cache
.gitignore: missing - add .firecrawl/ to ignore cache
“Authenticated via FIRECRAWL_API_KEY.” That is the whole fix. One flag, and the ambiguity disappears. Use --status and forget view-config exists.
(Yes, it says I have 132% of my credits left after running some crawls this month. I am choosing to accept this gift and not ask questions.)
The Almost-Right Command
Now the part that actually cost me something.

The key goes in an environment variable called FIRECRAWL_API_KEY. On bash or zsh, that means one line in your shell’s startup file:
# ~/.bashrc on bash, ~/.zshrc on zsh
export FIRECRAWL_API_KEY=fc-your-key-here
Then open a new terminal, or run source ~/.bashrc in the one you already have. The export does not reach backwards into shells that were already running.
Here is the trap, and it is not specific to any one shell. Typing export FIRECRAWL_API_KEY=fc-... straight at your prompt instead of putting it in the startup file works perfectly for the rest of that session and then evaporates when you close the window. Everything you test in the next five minutes succeeds. That is the worst possible failure mode, because success is exactly what convinces you to stop paying attention and move on.
If you are on fish
This is where it bit me, and fish makes the hole a little easier to fall into, because it has four variable scopes instead of one and the two that matter here differ by a single letter:
# Wrong. Dies when you close the terminal.
set -x FIRECRAWL_API_KEY fc-your-key-here
# Right. Persists across sessions.
set -Ux FIRECRAWL_API_KEY fc-your-key-here
-x exports it to child processes. -U makes it universal, which in fish means it survives the terminal closing. You need both.
The afternoon the almost-right command cost me
I asked an AI assistant for the fish command to set an environment variable permanently, and got back set -x. I closed the terminal, came back, was not authenticated, asked again, phrased it more emphatically, and got back set -x again. Somewhere in there I closed the tab with the API key on it.
I had not saved the key anywhere else, because I had just watched it get set successfully and had no reason to think I would need it again. So I rotated it and started over, which took the whole thing from a five-minute setup to something dumber. Copy the key into a password manager before you touch your shell config, and none of this can happen to you.
The generalizable lesson has nothing to do with fish. AI-assisted setup fails in a specific and nasty shape: it hands you the almost-right command. A wrong command errors out immediately and you fix it in ten seconds. An almost-right command succeeds, validates cleanly, and fails an hour later, after you have thrown away the thing you would need to recover. set -x is not a wrong answer to “set an environment variable in fish.” It is a wrong answer to “set it permanently,” and the word doing the work is the one the model dropped. Bash has the identical hole with a bare export at the prompt.
Whatever shell you are on, open a fresh terminal and confirm the thing you just set is still there before you trust it. That is the entire takeaway and it applies well beyond this tool.
Prove It Works Before You Trust It
Do not skip this. The whole point of the previous section is that a broken setup can look fine.

firecrawl scrape https://example.com --only-main-content -o smoke.md --timing
The -o flag is not optional in spirit. Without it the scraped page goes to stdout, and if you happen to smoke test something larger than example.com you will dump an entire article into your terminal. The --timing flag gives you a status line worth seeing.
A working setup looks like this:
Timing: {
"url": "https://example.com",
"requestTime": "2026-09-08T23:59:53.881Z",
"duration": "390ms",
"status": "success"
}
Scrape ID: 01a08376-c56f-73bf-bc28-411202792b72
And smoke.md contains actual markdown:
# Example Domain
This domain is for use in documentation examples without needing permission. Avoid use in operations.
[Learn more](https://iana.org/domains/example)
A broken key looks like this, and thankfully it is loud rather than silent:
Error: Unauthorized: Invalid token
One more thing while you are here. Scrapes with multiple URLs get cached into a .firecrawl/ directory in your working directory, and the --status output nags you about this every time, whether or not you are anywhere near a git repo. When you are in one, listen to it:
echo ".firecrawl/" >> .gitignore
Committing a cache directory full of scraped pages is a bad afternoon waiting to happen, and it is the kind of thing you only notice three weeks later in a diff.
The Setup Summary
Stripped of everything above, the working setup is four lines:
npm install -g firecrawl-cli
echo 'export FIRECRAWL_API_KEY=fc-your-key-here' >> ~/.bashrc && source ~/.bashrc
firecrawl --status
firecrawl scrape https://example.com --only-main-content -o smoke.md
On zsh, swap ~/.bashrc for ~/.zshrc. On fish, line two is set -Ux FIRECRAWL_API_KEY fc-your-key-here.
Install, key, verify, prove. Nothing writes into your editors, nothing sits in your agent’s context, and you know it works because you watched it work.
The mess I spent an afternoon in was not really Firecrawl’s mess. It was two things that are now true of most developer tools: the installer has become a growth channel, so its default path is maximal rather than minimal, and the auth surface has grown enough paths that the status readout can no longer say one clear thing. Both of those will be true of the next tool you install too. The defense is the same either way, which is to install the binary, wire it up yourself, and run one command that proves it before you build anything on top of it.
Next in this series I start actually using the thing, which means the first real recipe. This post exists so that one does not have to open with a paragraph about environment variables.
* This website contains affiliate links. This means that if you click on a link and purchase a product or service, I may receive a small commission at no extra cost to you. Please note that I only recommend products and services that I believe in and that will add value to my readers. Not all links on this website are affiliate links. Learn more.
