Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions docs/en/tools/search-research/x402searchtool.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: x402search Tool
description: The `X402SearchTool` searches 14,000+ indexed API services by natural language capability query via x402 protocol.
icon: magnifying-glass
mode: "wide"
---

# `X402SearchTool`

## Description

The `X402SearchTool` enables CrewAI agents to discover API services by natural language capability query. It searches 14,000+ indexed APIs across crypto, DeFi, NFT, weather, finance, AI, and more — returning matching services with names, descriptions, and endpoints.

**Cost:** $0.01 USDC per query via [x402 protocol](https://x402.org) on Base mainnet — paid automatically, no API key required.

Coinbase Bazaar lists services. x402search searches them by capability — they are complementary, not competing.

## Installation
```shell
uv add crewai
```

No API key required. Payment of $0.01 USDC is handled automatically via x402 protocol on Base mainnet. The agent's wallet must hold USDC on Base mainnet (`eip155:8453`).

## Example
```python
from crewai import Agent, Task, Crew
from crewai.tools import X402SearchTool

search_tool = X402SearchTool()

agent = Agent(
role="API Researcher",
goal="Find the best APIs for a given capability",
backstory="Expert at discovering and evaluating API services",
tools=[search_tool],
verbose=True,
)

task = Task(
description="Find APIs for real-time cryptocurrency price data.",
expected_output="A list of 5 API services with names, descriptions, and endpoints.",
agent=agent,
)

crew = Crew(agents=[agent], tasks=[task])
result = crew.kickoff()
```

### Direct usage
```python
from crewai.tools import X402SearchTool

tool = X402SearchTool()
print(tool.run(query="token price", limit=5))
```

## Parameters

### Initialization Parameters

No required initialization parameters.

### Run Parameters

- `query` (str, required): Natural language capability query (2–200 chars).
- `limit` (int, default `5`, range 1–10): Maximum number of results to return.

## Best queries

| Query | Approx results |
|-------|---------------|
| `crypto` | 112 |
| `token price` | 88 |
| `crypto market data` | 10 |
| `btc price` | 8 |

## Output format

Returns a human-readable list of matching API services with:
- Service name
- Description (truncated to 120 chars)
- Endpoint URL

## Payment

Payment is handled automatically via the [x402 protocol](https://x402.org). The agent's wallet pays $0.01 USDC per query on Base mainnet. No API keys or subscriptions required.

## Related links

- x402search API: https://x402search.xyz
- x402 protocol: https://x402.org
- MCP package: `x402search-mcp` on npm and PyPI
3 changes: 3 additions & 0 deletions lib/crewai/src/crewai/tools/x402search_tool/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .x402search_tool import X402SearchTool

__all__ = ["X402SearchTool"]
93 changes: 93 additions & 0 deletions lib/crewai/src/crewai/tools/x402search_tool/x402search_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json
import logging
from typing import Any, Optional, Type

import requests
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

logger = logging.getLogger(__name__)

X402SEARCH_URL = "https://x402search.xyz/v1/search"


class X402SearchToolSchema(BaseModel):
"""Input for X402SearchTool."""

query: str = Field(
...,
description=(
"Natural language query to find API services by capability. "
"Examples: 'token price', 'crypto market data', 'NFT metadata', "
"'weather forecast', 'sentiment analysis', 'btc price'"
),
)
limit: Optional[int] = Field(
default=5,
ge=1,
le=10,
description="Maximum number of results to return (1-10, default 5).",
)


class X402SearchTool(BaseTool):
name: str = "Search APIs by capability"
description: str = (
"Search for API services and data providers by natural language capability query. "
"Searches 14,000+ indexed APIs across crypto, DeFi, NFT, weather, finance, AI, and more. "
"Returns matching services with names, descriptions, and endpoints. "
"Cost: $0.01 USDC per query via x402 protocol on Base mainnet — paid automatically. "
"Use this to discover what APIs exist for a given capability before calling them. "
"Best queries: 'token price' (88 results), 'crypto market data' (10), 'btc price' (8)."
)
args_schema: Type[BaseModel] = X402SearchToolSchema
base_url: str = X402SEARCH_URL

def _run(self, **kwargs: Any) -> Any:
query = kwargs.get("query", "")
limit = kwargs.get("limit", 5)

if not query or len(query.strip()) < 2:
return "Please provide a search query of at least 2 characters."

try:
response = requests.get(
self.base_url,
params={"q": query.strip()},
timeout=15,
)
response.raise_for_status()
data = response.json()

except requests.Timeout:
return f"x402search timed out for query '{query}'. Try again or use a broader term."
except requests.HTTPError as e:
return f"x402search API error: {e.response.status_code}"
except requests.RequestException as e:
return f"x402search connection failed: {e}"

results = data.get("results", data) if isinstance(data, dict) else data
if not isinstance(results, list):
return "Unexpected response format from x402search."

results = results[:limit]

if not results:
return (
f"No API services found for '{query}'. "
"Try broader terms — 'crypto' returns 112 results, 'token price' returns 88."
)

lines = [f"Found API services for '{query}':\n"]
for i, r in enumerate(results, 1):
name = r.get("name") or r.get("api_name", "Unknown")
desc = r.get("description") or r.get("accepts", "")
url = r.get("url") or r.get("base_url", "")
lines.append(f"{i}. {name}")
if desc:
lines.append(f" {desc[:120]}{'...' if len(desc) > 120 else ''}")
if url:
lines.append(f" {url}")
lines.append("")

return "\n".join(lines).strip()