Loading...
Loading...
Reference skill for building production-ready crw integrations. Covers verb selection, call surfaces (CLI/MCP/REST), post-filtering strategies, context-window hygiene, Hybrid RAG patterns, common pitfalls, and crw-specific operational considerations (search backend limits, renderer pool, proxy rotation). Load this when writing application code that embeds crw, designing a multi-step agent workflow, or debugging an integration that isn't behaving as expected.
npx skill4agent add us/crw crw-best-practices| Need | Verb | Notes |
|---|---|---|
| You have a question/topic, not a URL | search | Own search backend, no API key required. Returns titles + URLs + snippets. Add |
| You have one (or a few) known URLs | scrape | Returns markdown, HTML, links, or structured JSON. JS auto-detected. |
| You need to discover which URLs exist on a site | map | Fast URL discovery via sitemap + BFS. No content fetched. Use before committing to a crawl. |
| You need content from many pages under a site | crawl | Async BFS job. Poll with |
| The source is a local file (PDF) | parse | |
| You need a typed JSON object from a page | extract | |
| You want to detect what changed on a page | watch / diff | |
searchscrapesearch --jsoncrw_searchcrw scrapemapcrawlmap "https://docs.example.com"/docs/api/authscrapecrwcrw search "query" --json --limit 5
crw scrape "https://example.com" --format json
crw map "https://docs.example.com"
crw scrape "report.pdf" # local PDF auto-detectedcrw_searchcrw_scrapecrw_mapcrw_crawlcrw_parse_filecrw_scrape(url="https://example.com", formats=["markdown"], onlyMainContent=true)
crw_search(query="query", limit=5)
crw_map(url="https://docs.example.com", limit=200)crw_maptruncated: truemaxLength: 0limit: 0/v1/scrape/v1/searchapi_url# Python SDK (pip install crw)
from crw import CrwClient
client = CrwClient(api_url="https://api.fastcrw.com", api_key="crw_live_...")
result = client.scrape("https://example.com", formats=["markdown"])
results = client.search("AI news", limit=10)
# Drop-in for Firecrawl SDK
from firecrawl import FirecrawlApp
app = FirecrawlApp(api_url="https://api.fastcrw.com", api_key="crw_live_...")# Rely on position, not score
top = [r for r in results if r['position'] <= 5]keywords = {'commercializ', 'battery', 'production', '2025', '2026'}
relevant = [r for r in results
if any(kw in r['description'].lower() for kw in keywords)]for para in markdown.split('\n\n'):
if len(para) > 60 and any(kw in para.lower() for kw in keywords):
print(para)import anthropic
def is_relevant(snippet: str, query: str) -> dict:
"""Returns {is_match: bool, confidence: float, reasoning: str}"""
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-haiku-4-5", # cheap model for classification
max_tokens=128,
messages=[{
"role": "user",
"content": (
f"Query: {query}\n\n"
f"Snippet: {snippet[:500]}\n\n"
"Does this snippet directly answer or provide evidence for the query? "
"Reply with JSON only: {\"is_match\": true/false, \"confidence\": 0-1, "
"\"reasoning\": \"one sentence\"}"
)
}]
)
import json
return json.loads(msg.content[0].text)crw search --jsoncrw scrape --format jsonprint().crw//tmp/crw scrape -o .crw/page.jsongrepcrw search "query" → top-N results (titles + snippets)
→ scrape top 3-5 full pages → filter to relevant paragraphs
→ embed filtered paragraphs → merge with local vector store
→ retrieve top-K chunks → feed to generation modelcrw_crawljsonSchemafrom crw import CrwClient
client = CrwClient() # embedded mode, no server
def retrieve_and_chunk(query: str, top_n: int = 5) -> list[str]:
results = client.search(query, limit=top_n)
chunks = []
for r in results:
# Scrape full page if the snippet isn't sufficient
page = client.scrape(r['url'], formats=['markdown'])
md = page.get('markdown', '') or ''
# Split into paragraphs, keep non-trivial ones
for para in md.split('\n\n'):
para = para.strip()
if len(para) > 100:
chunks.append(para)
return chunkscrw search| Problem | Impact | Solution |
|---|---|---|
| Piping raw JSON into context | 50K-500K chars enters context; token waste, reasoning degradation | Always filter in a Python subprocess — see crw-dynamic-search |
Trusting | The search backend's scores are engine-dependent, often | Triage by |
| Crawling without mapping first | Committing to a 500-page crawl when you needed 20 pages | Always |
| JS rendering on every scrape | Unnecessary browser spawn on plain-HTML pages; slow | crw auto-detects SPAs — don't add |
| Blocking on crawl job poll | Agent hangs waiting for async crawl | Set a poll interval (5-10s), set |
Ignoring | Missing content from MCP calls; silent data loss | Check for |
Writing one-shot scripts to | Wasteful; file left behind | Use heredocs for one-shot filtering; only write data (JSON results) to |
Scraping | 403/empty response; wasted call | crw respects |
crw setup --local--category news--time-range weekdocker compose --profile heavy[renderer.chrome] ws_urlCRW_CDP_URLcrw scrape --js--proxy URLproxyproxyRotationround_robinrandomsticky_per_hostproxyRotation: "sticky_per_host"scrapecrawlmapsearch| Feature | Self-hosted | Managed ( |
|---|---|---|
| Search | Requires a local search-backend sidecar | Included (managed backend) |
| Proxy pool | BYOP via config | Managed proxy network |
| Rate limiting | Token-bucket (configurable) | Per-plan limits; |
| Credits | N/A | 500 one-time lifetime free credits |
| AGPL obligation | Applies if you expose to third parties | Carve-out included |
COMPATIBILITY-firecrawl.md