Loading...
Loading...
Stand up your own fastCRW API server — single binary, Docker, or docker-compose with a bundled search-backend sidecar. Use when the user wants to run crw locally or on their own infra, configure renderers/proxies/ auth/LLM extraction, or understand the embedded vs proxy MCP modes.
npx skill4agent add us/crw crw-self-hostapi.fastcrw.comcrw-mcpnpx crw-mcp # zero install (npm; embedded engine, ~6 MB RAM)
brew install us/crw/crw-mcp # Homebrew
cargo install crw-mcp # Cargo (~17 MB, full embedded)
docker run -i ghcr.io/us/crw crw-mcp # Docker
pip install crw # Python SDK (auto-downloads binary on first use)cargo build --profile release-small --no-default-features -p crw-mcpcrwbrew install us/crw/crw
curl -fsSL https://fastcrw.com/install | CRW_BINARY=crw sh
cargo install crw-cli
# APT (Debian/Ubuntu):
curl -fsSL https://apt.fastcrw.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/crw.gpg
echo "deb [signed-by=/usr/share/keyrings/crw.gpg] https://apt.fastcrw.com stable main" \
| sudo tee /etc/apt/sources.list.d/crw.list
sudo apt update && sudo apt install crwcrw-serverbrew install us/crw/crw-server
curl -fsSL https://fastcrw.com/install | CRW_BINARY=crw-server sh
docker run -p 3000:3000 ghcr.io/us/crwcrw servecrw serve # reads config.default.toml, listens on :3000
crw serve --port 3001 # custom port (-p short form also accepted)
crw serve --config myconfig.toml # load specific config file
CRW_PORT=3001 crw-server # env-var alternative for the standalone binarydocker run -p 3000:3000 ghcr.io/us/crw
curl http://localhost:3000/v1/scrape \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'docker compose up -d # http + LightPanda + search backend
docker compose --profile heavy up -d # + Chrome fallback
docker compose -f docker-compose.yml \
-f docker-compose.stealth.yml --profile stealth up -d # + browserless stealth tierdocker compose upcrwlightpandasearxngcrwsearxngcompose upcurl http://localhost:3000/health # → {"status":"ok",...}
curl -X POST http://localhost:3000/v1/search \
-H "Content-Type: application/json" \
-d '{"query":"fastCRW","limit":5}' # own backend, no API keydocker-compose.ymlsearxng/searxng:2026.5.9-0cba32c15/v1/searchcrwhttp://searxng:8080config.docker.toml[search] searxng_url = "http://searxng:8080"docker-compose.override.ymlservices:
searxng:
ports:
- "127.0.0.1:8888:8080"CRW_SEARCH__SEARXNG_URL=http://my-searxng:8080 crw-serversearxng_url/v1/searchsearch_disabledconfig.default.tomlconfig.default.tomlCRW_CONFIG=<name><name>.tomlconfig.docker.tomlCRW_CONFIG=config.dockerdocker-compose.ymlCRW_<SECTION>__<KEY>CRW_SERVER__PORT=8080
CRW_RENDERER__MODE=chrome
CRW_RENDERER__POOL_SIZE=8
CRW_SEARCH__SEARXNG_URL=http://localhost:8080
CRW_CRAWLER__PROXY=http://user:pass@proxy:8080
CRW_EXTRACTION__LLM__API_KEY=sk-...
CRW_AUTH__API_KEYS='["key-one","key-two"]' # JSON array as string[renderer][renderer]
mode = "auto" # auto | lightpanda | chrome | playwright | none
pool_size = 4 # browser context pool size
page_timeout_ms = 30000autolightpandachromeplaywrightnone[renderer.lightpanda]
ws_url = "ws://127.0.0.1:9222/"
[renderer.chrome]
ws_url = "ws://127.0.0.1:9223/"[search][search]
enabled = true
# searxng_url = "http://localhost:8080" # required for /v1/search to work
timeout_ms = 15000
default_limit = 5
max_limit = 20[crawler][crawler]
# Single proxy:
# proxy = "http://user:pass@proxy:8080"
# proxy = "socks5://user:pass@proxy:1080"
# Pool with rotation:
# proxy_list = ["http://user:pass@a:8080", "http://user:pass@b:8080"]
# proxy_rotation = "sticky_per_host" # sticky_per_host | round_robin | random
# Stealth mode (rotate UA + inject browser-like headers):
# [crawler.stealth]
# enabled = true
# inject_headers = true
# jitter_factor = 0.2[extraction.llm]formats: ["json"]formats: ["summary"]/v1/searchanswer: true[extraction.llm]
provider = "anthropic" # "anthropic" | "openai" | "openai-responses" | "deepseek" | "azure" | "openai-compatible"
api_key = "sk-..." # or CRW_EXTRACTION__LLM__API_KEY env var
model = "claude-sonnet-4-20250514"
max_tokens = 4096
# base_url = "https://custom-endpoint.example.com" # Chat Completions or Responses endpoints
max_concurrency = 4
max_html_bytes = 100000
# DeepSeek example:
# provider = "deepseek"
# api_key = "..."
# model = "deepseek-chat"
# base_url = "https://api.deepseek.com/v1"
# Azure OpenAI example:
# provider = "azure"
# api_key = "..."
# model = "gpt-4o-mini" # Azure deployment name
# base_url = "https://<resource>.openai.azure.com"
# azure_api_version = "2024-05-01-preview"[auth][auth]
api_keys = ["crw-key-one", "crw-key-two"]api_keysapi.fastcrw.com[document][document]
enabled = true
max_pages = 0 # 0 = no limit
max_upload_bytes = 52428800 # 50 MiB cap for POST /v2/parse uploads
upload_concurrency = 4
max_concurrent_parses = 4
parse_timeout_ms = 30000
sandbox = false # set true in Docker (untrusted uploads)crw-mcpCRW_API_URLCRW_API_URLnpx crw-mcp # embedded
claude mcp add crw -- npx -y crw-mcp # Claude Code, embeddedCRW_API_URLcrw-serverapi.fastcrw.comCRW_API_URL=https://api.fastcrw.com CRW_API_KEY=crw_live_... npx crw-mcp
# Claude Code:
claude mcp add crw \
-e CRW_API_URL=https://api.fastcrw.com -e CRW_API_KEY=crw_live_... \
-- npx -y crw-mcp# Health (no auth)
curl http://localhost:3000/health
# → {"status":"ok","version":"..."}
# Scrape (no auth on default self-host)
curl -X POST http://localhost:3000/v1/scrape \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","formats":["markdown"]}' | jq .success
# → true
# Search (requires a search-backend sidecar via docker compose up)
curl -X POST http://localhost:3000/v1/search \
-H "Content-Type: application/json" \
-d '{"query":"hello","limit":3}' | jq .success
# → true (or 400 search_disabled if search backend not wired up)SEARXNG_SECRET_KEY.envopenssl rand -hex 32[document] sandbox = trueconfig.docker.toml[auth] api_keysrate_limit_rps = 10config.default.toml0