On this page
Anthropic's Claude API supports native tool use -- you define tools with JSON schemas, and Claude decides when to invoke them during a conversation. CrawlForge's 26 web scraping tools are a natural fit: they give Claude the ability to search the web, extract content, scrape structured data, and conduct deep research, all through the standard tool_use API.
This guide walks you through defining CrawlForge tools for the Claude API, handling tool use responses, and building a production-grade research assistant.
Table of Contents
- Prerequisites
- How Claude Tool Use Works with CrawlForge
- Step 1: Define CrawlForge Tool Schemas
- Step 2: Handle the Tool Use Loop
- Step 3: Build a Research Assistant
- Advanced: Streaming with Tool Use
- Credit Cost Breakdown
- Best Practices
- Frequently Asked Questions
- Next Steps
Prerequisites
npm install @anthropic-ai/sdk dotenv# .env
ANTHROPIC_API_KEY=sk-ant-xxxxx
CRAWLFORGE_API_KEY=cf_live_xxxxxGet your CrawlForge API key at crawlforge.dev/signup -- 1,000 free credits included. For Claude API access, visit console.anthropic.com and follow the API getting started guide.
How Claude Tool Use Works with CrawlForge
Claude's tool use follows a request-response loop:
- You send a message with tool definitions and a user prompt
- Claude responds with either text or a
tool_usecontent block - You execute the tool (call CrawlForge API) and return the result
- Claude incorporates the result and continues its response
You -> Claude: "What's on the Hacker News front page?"
Claude -> You: tool_use { name: "extract_content", input: { url: "https://news.ycombinator.com" } }
You -> CrawlForge: POST /api/v1/tools/extract_content { url: "..." }
CrawlForge -> You: { content: "..." }
You -> Claude: tool_result { content: "..." }
Claude -> You: "Here are the top stories on Hacker News right now: ..."
Step 1: Define CrawlForge Tool Schemas
Define the tools Claude can use. Each tool needs a name, description, and input_schema (JSON Schema format):
// lib/tool-definitions.ts
import Anthropic from '@anthropic-ai/sdk';
export const crawlforgeTools: Anthropic.Tool[] = [
{
name: 'search_web',
description: 'Search Google and return top results. Use when you need to find web pages about a topic. Returns titles, URLs, and snippets. Costs 5 credits.',
input_schema: {
type: 'object' as const,
properties: {
query: {
type: 'string',
description: 'The search query',
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 5)',
},
},
required: ['query'],
},
},
{
name: 'extract_content',
description: 'Extract the main readable content from a web page. Returns clean text without navigation, ads, or boilerplate. Use when you need to read a specific URL. Costs 2 credits.',
input_schema: {
type: 'object' as const,
properties: {
url: {
type: 'string',
description: 'The full URL to extract content from',
},
},
required: ['url'],
},
},
{
name: 'scrape_structured',
description: 'Extract specific data from a page using CSS selectors. Use when you need structured data like prices, names, or lists. Costs 2 credits.',
input_schema: {
type: 'object' as const,
properties: {
url: {
type: 'string',
description: 'The URL to scrape',
},
selectors: {
type: 'object',
description: 'Map of field names to CSS selectors',
additionalProperties: { type: 'string' },
},
},
required: ['url', 'selectors'],
},
},
{
name: 'fetch_url',
description: 'Fetch raw HTML from a URL. Cheapest option at 1 credit. Use for APIs or when you need the raw HTML.',
input_schema: {
type: 'object' as const,
properties: {
url: {
type: 'string',
description: 'The URL to fetch',
},
},
required: ['url'],
},
},
{
name: 'extract_metadata',
description: 'Get page metadata including title, description, Open Graph tags, and other meta information. Costs 1 credit.',
input_schema: {
type: 'object' as const,
properties: {
url: {
type: 'string',
description: 'The URL to extract metadata from',
},
},
required: ['url'],
},
},
];Step 2: Handle the Tool Use Loop
The core pattern: send messages to Claude, check if it wants to use a tool, execute the tool via CrawlForge, and return the result.
// lib/agent.ts
import Anthropic from '@anthropic-ai/sdk';
import { crawlforgeTools } from './tool-definitions';
const client = new Anthropic();
const CRAWLFORGE_BASE = 'https://crawlforge.dev/api/v1/tools';
// Execute a CrawlForge tool
async function executeTool(
name: string,
input: Record<string, unknown>
): Promise<string> {
const response = await fetch(`${CRAWLFORGE_BASE}/${name}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CRAWLFORGE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(input),
});
if (!response.ok) {
return JSON.stringify({ error: `HTTP ${response.status}: ${response.statusText}` });
}
const data = await response.json();
return JSON.stringify(data);
}
// Run Claude with CrawlForge tools
export async function askClaude(prompt: string): Promise<string> {
const messages: Anthropic.MessageParam[] = [
{ role: 'user', content: prompt },
];
// Tool use loop: keep going until Claude gives a final text response
while (true) {
const response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 4096,
tools: crawlforgeTools,
messages,
});
// Check if Claude wants to use a tool
if (response.stop_reason === 'tool_use') {
// Add Claude's response to message history
messages.push({ role: 'assistant', content: response.content });
// Execute each tool call and collect results
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await executeTool(
block.name,
block.input as Record<string, unknown>
);
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: result,
});
}
}
// Return tool results to Claude
messages.push({ role: 'user', content: toolResults });
} else {
// Claude gave a final text response
const textBlock = response.content.find(b => b.type === 'text');
return textBlock ? textBlock.text : '';
}
}
}
// Usage
const answer = await askClaude(
'What are the top 3 trending repositories on GitHub right now?'
);
console.log(answer);This loop handles multi-step tool use automatically. Claude might search, then extract content from a result, then search again -- the loop continues until it produces a final text response.
Step 3: Build a Research Assistant
Wrap the agent in a more structured application:
// research-assistant.ts
import { askClaude } from './lib/agent';
async function researchTopic(topic: string, depth: 'quick' | 'thorough' = 'quick') {
const systemPrompt = depth === 'thorough'
? `Research this topic thoroughly. Search for at least 3 different sources,
read each one, and provide a comprehensive summary with citations.
Topic: ${topic}`
: `Quickly answer this question using web search if needed.
Be concise and cite your source. Topic: ${topic}`;
console.log(`Researching: ${topic} (mode: ${depth})`);
const result = await askClaude(systemPrompt);
console.log(result);
return result;
}
// Quick research: ~7 credits (search + 1 extract)
await researchTopic('What is the current version of Next.js?', 'quick');
// Thorough research: ~11-15 credits (search + 3 extracts)
await researchTopic(
'Compare the performance of Bun vs Node.js for HTTP servers in 2026',
'thorough'
);Advanced: Streaming with Tool Use
For a better user experience, use streaming to show Claude's thinking in real time:
// lib/streaming-agent.ts
import Anthropic from '@anthropic-ai/sdk';
import { crawlforgeTools } from './tool-definitions';
const client = new Anthropic();
const CRAWLFORGE_BASE = 'https://crawlforge.dev/api/v1/tools';
async function executeTool(
name: string,
input: Record<string, unknown>
): Promise<string> {
const response = await fetch(`${CRAWLFORGE_BASE}/${name}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CRAWLFORGE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(input),
});
return JSON.stringify(await response.json());
}
export async function streamWithTools(prompt: string) {
const messages: Anthropic.MessageParam[] = [
{ role: 'user', content: prompt },
];
while (true) {
const stream = client.messages.stream({
model: 'claude-sonnet-4-20250514',
max_tokens: 4096,
tools: crawlforgeTools,
messages,
});
// Collect streamed text
let hasToolUse = false;
stream.on('text', (text) => {
process.stdout.write(text); // Stream to terminal in real time
});
const response = await stream.finalMessage();
if (response.stop_reason === 'tool_use') {
hasToolUse = true;
messages.push({ role: 'assistant', content: response.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === 'tool_use') {
console.log(`\n[Calling ${block.name}...]\n`);
const result = await executeTool(
block.name,
block.input as Record<string, unknown>
);
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: result,
});
}
}
messages.push({ role: 'user', content: toolResults });
}
if (!hasToolUse) break;
}
}
// Usage: streams text to stdout as Claude generates it
await streamWithTools('What is CrawlForge MCP and how does it compare to Firecrawl?');Credit Cost Breakdown
| Workflow | Tools Used | Credits |
|---|---|---|
| Quick answer (1 page) | extract_content | 2 |
| Search + read top result | search_web + extract_content | 7 |
| Thorough research (3 sources) | search_web + 3x extract_content | 11 |
| Structured data extraction | scrape_structured | 2 |
| Page metadata check | extract_metadata | 1 |
| Raw HTML fetch | fetch_url | 1 |
| Deep multi-source report | deep_research | 10 |
The Free tier (1,000 one-time credits) supports approximately 140 single-page extractions or 90 search-and-read workflows to start. The Hobby plan ($19/month, 5,000 credits) is ideal for development and light production use.
Best Practices
Write descriptive tool descriptions. Claude uses the description field to decide which tool to call. Include what the tool does, when to use it, and its credit cost. "Extract the main readable content from a web page" is better than "Get content".
Include credit costs in descriptions. When Claude knows that fetch_url costs 1 credit and deep_research costs 10, it naturally chooses the cheaper option for simple tasks.
Handle errors gracefully. Return error messages as tool results rather than throwing exceptions. Claude can adapt its strategy when a tool fails -- for example, trying a different URL or rephrasing a search.
Set max_tokens appropriately. Web content can be long. Set max_tokens to at least 4096 to give Claude room to incorporate tool results into comprehensive responses.
Use system prompts to guide tool use. Tell Claude when to search vs. when to directly access a known URL. This prevents unnecessary search_web calls (5 credits) when a direct extract_content (2 credits) would suffice.
Frequently Asked Questions
Can I use CrawlForge with Claude 3.5 Haiku for lower costs?
Yes. All Claude models that support tool use work with CrawlForge tools. Haiku is cheaper per token but may need more explicit instructions to select the right tool. Claude Sonnet provides the best balance of cost and tool-use accuracy.
How do I handle rate limits?
CrawlForge's API includes rate limiting headers (X-RateLimit-Remaining). If you hit a 429 response, add a retry with exponential backoff. For high-volume use, the Professional plan includes higher rate limits.
Can Claude call multiple CrawlForge tools in one turn?
Yes. Claude can request multiple tool uses in a single response. The tool use loop in Step 2 handles this -- it iterates over all tool_use blocks and returns all results at once.
What happens when CrawlForge credits run out?
The API returns a 402 Payment Required error. Return this as a tool result so Claude can inform the user. You can check remaining credits via the dashboard or the credits API endpoint.
Next Steps
You now have a Claude-powered application with live web access. Explore further:
- CrawlForge Quick Start for native MCP integration with Claude Code
- All 26 tools explained with credit costs and usage examples
- Building an AI Research Assistant with Claude and CrawlForge
- CrawlForge vs Firecrawl comparison for choosing the right tool
Give Claude access to the live web. Start free with 1,000 credits -- no credit card required.
Try this yourself — no signup needed
Run any of CrawlForge's 27 scraping and extraction tools in the playground, then start free with 1,000 credits.
1,000 free credits • Refills monthly • No credit card required
Tags
About the Author
Stay updated with the latest insights
Get tutorials, product updates, and web scraping tips delivered to your inbox.
No spam. Unsubscribe anytime.