diff --git a/camel/toolkits/search_toolkit.py b/camel/toolkits/search_toolkit.py
index 9b6013d0b9..db577a2fa6 100644
--- a/camel/toolkits/search_toolkit.py
+++ b/camel/toolkits/search_toolkit.py
@@ -55,6 +55,55 @@ def __init__(
super().__init__(timeout=timeout)
self.exclude_domains = exclude_domains
+ @api_keys_required(
+ [
+ (None, "SERPER_API_KEY"),
+ ]
+ )
+ def search_serper(
+ self,
+ query: str,
+ page: int = 1,
+ location: str = "United States",
+ ) -> Dict[str, Any]:
+ r"""Use Serper.dev API to perform Google search.
+
+ Args:
+ query (str): The search query.
+ page (int): The page number of results to retrieve. (default: :obj:`1`)
+ location (str): The location for the search results.
+ (default: :obj:`"United States"`)
+
+ Returns:
+ Dict[str, Any]: The search result dictionary containing 'organic',
+ 'peopleAlsoAsk', etc.
+ """
+ import json
+
+ SERPER_API_KEY = os.getenv("SERPER_API_KEY")
+
+ url = "https://google.serper.dev/search"
+
+ payload = json.dumps(
+ {
+ "q": query,
+ "location": location,
+ "page": page,
+ }
+ )
+
+ headers = {
+ "X-API-KEY": SERPER_API_KEY,
+ "Content-Type": "application/json",
+ }
+
+ try:
+ response = requests.post(url, headers=headers, data=payload)
+ response.raise_for_status()
+ return response.json()
+ except requests.exceptions.RequestException as e:
+ raise RuntimeError(f"Error making request to Serper: {e}")
+
@dependencies_required("wikipedia")
def search_wiki(self, entity: str) -> str:
r"""Search the entity in WikiPedia and return the summary of the
@@ -376,8 +425,6 @@ def search_brave(
Dict[str, Any]: A dictionary representing a search result.
"""
- import requests
-
BRAVE_API_KEY = os.getenv("BRAVE_API_KEY")
url = "https://api.search.brave.com/res/v1/web/search"
@@ -519,8 +566,6 @@ def search_google(
"""
from urllib.parse import quote
- import requests
-
# Validate input parameters
if not isinstance(start_page, int) or start_page < 1:
raise ValueError("start_page must be a positive integer")
@@ -1171,6 +1216,7 @@ def search_alibaba_tongxiao(
message. Each result contains title, snippet, url and other
metadata.
"""
+
TONGXIAO_API_KEY = os.getenv("TONGXIAO_API_KEY")
# Validate query length
@@ -1452,6 +1498,7 @@ def get_tools(self) -> List[FunctionTool]:
representing the functions in the toolkit.
"""
return [
+ FunctionTool(self.search_serper),
FunctionTool(self.search_wiki),
FunctionTool(self.search_linkup),
FunctionTool(self.search_google),
diff --git a/docs/mintlify/reference/camel.toolkits.search_toolkit.mdx b/docs/mintlify/reference/camel.toolkits.search_toolkit.mdx
index a2ae4ed05d..c979f00910 100644
--- a/docs/mintlify/reference/camel.toolkits.search_toolkit.mdx
+++ b/docs/mintlify/reference/camel.toolkits.search_toolkit.mdx
@@ -32,6 +32,31 @@ Initializes the SearchToolkit.
- **timeout** (float): Timeout for API requests in seconds. (default: :obj:`None`)
- **exclude_domains** (Optional[List[str]]): List of domains to exclude from search results. Currently only supported by the `search_google` function. (default: :obj:`None`)
+
+
+### search_serper
+
+```python
+def search_serper(
+ self,
+ query: str,
+ page: int = 1,
+ location: str = 'United States'
+):
+```
+
+Use Serper.dev API to perform Google search.
+
+**Parameters:**
+
+- **query** (str): The search query.
+- **page** (int): The page number of results to retrieve. (default: :obj:`1`)
+- **location** (str): The location for the search results. (default: :obj:`"United States"`)
+
+**Returns:**
+
+ Dict[str, Any]: The search result dictionary containing 'organic', 'peopleAlsoAsk', etc.
+
### search_wiki
diff --git a/docs/reference/camel.toolkits.search_toolkit.md b/docs/reference/camel.toolkits.search_toolkit.md
index 91e1a4c1e9..6c220f7359 100644
--- a/docs/reference/camel.toolkits.search_toolkit.md
+++ b/docs/reference/camel.toolkits.search_toolkit.md
@@ -32,6 +32,31 @@ Initializes the SearchToolkit.
- **timeout** (float): Timeout for API requests in seconds. (default: :obj:`None`)
- **exclude_domains** (Optional[List[str]]): List of domains to exclude from search results. Currently only supported by the `search_google` function. (default: :obj:`None`)
+
+
+### search_serper
+
+```python
+def search_serper(
+ self,
+ query: str,
+ page: int = 1,
+ location: str = 'United States'
+):
+```
+
+Use Serper.dev API to perform Google search.
+
+**Parameters:**
+
+- **query** (str): The search query.
+- **page** (int): The page number of results to retrieve. (default: :obj:`1`)
+- **location** (str): The location for the search results. (default: :obj:`"United States"`)
+
+**Returns:**
+
+ Dict[str, Any]: The search result dictionary containing 'organic', 'peopleAlsoAsk', etc.
+
### search_wiki
diff --git a/examples/toolkits/search_toolkit.py b/examples/toolkits/search_toolkit.py
index 7f340247ef..c1fbc7146b 100644
--- a/examples/toolkits/search_toolkit.py
+++ b/examples/toolkits/search_toolkit.py
@@ -12,6 +12,8 @@
# limitations under the License.
# ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
+import os
+
from pydantic import BaseModel
from camel.agents import ChatAgent
@@ -419,6 +421,66 @@ class PersonInfo(BaseModel):
""" # noqa: E501
+# Example using Serper search
+if os.getenv("SERPER_API_KEY"):
+ serper_response = SearchToolkit().search_serper(
+ query="Apple Inc",
+ page=1,
+ )
+ print(serper_response)
+"""
+===============================================================================
+{'searchParameters': {'q': 'Apple Inc', 'type': 'search', 'page': 1,
+'location': 'United States', 'engine': 'google', 'gl': 'us'},
+'organic': [{'title': 'Apple', 'link': 'https://www.apple.com/',
+'snippet': 'Discover the innovative world of Apple and shop everything
+iPhone, iPad, Apple Watch, Mac, and Apple TV, plus explore accessories,
+entertainment, ...', 'position': 1}, {'title': 'Apple Inc.', 'link':
+'https://en.wikipedia.org/wiki/Apple_Inc.', 'snippet': 'Apple Inc. is
+an American multinational technology company headquartered in
+Cupertino, California, in Silicon Valley, best known for its consumer
+electronics, ...', 'position': 2}, {'title': 'AAPL: Apple Inc Stock
+Price Quote - NASDAQ GS', 'link':
+'https://www.bloomberg.com/quote/AAPL:US', 'snippet': 'Apple Inc.
+designs, manufactures, and markets smartphones, personal computers,
+tablets, wearables and accessories, and sells a variety of related
+accessories.', 'position': 3}, {'title': 'Apple Inc. | History,
+Products, Headquarters, & Facts', 'link':
+'https://www.britannica.com/money/Apple-Inc', 'snippet': 'Apple Inc. is
+an American multinational technology company that revolutionized the
+technology sector through its innovation of computer software, personal
+...', 'date': '3 days ago', 'position': 4}, {'title': 'Apple Inc.
+(AAPL)', 'link': 'https://finance.yahoo.com/quote/AAPL/', 'snippet':
+'Apple Inc. designs, manufactures, and markets smartphones, personal
+computers, tablets, wearables, and accessories worldwide.', 'position':
+5}, {'title': 'What the heck is going on at Apple?', 'link':
+'https://www.cnn.com/2025/12/06/tech/apple-tim-cook-leadership-changes',
+'snippet': 'Now the company known for its steadiness is going through a
+shakeup at the top, as both Apple and the tech industry at large are at
+a crossroads ...', 'date': '1 day ago', 'position': 6}, {'title':
+'Apple Inc. (AAPL) Stock Price Today - WSJ', 'link':
+'https://www.wsj.com/market-data/quotes/AAPL?gaa_at=eafs&gaa_n=AWEtsqeZ
+rQcR92j11_hQeDtRlWcl9tKefoYwgR1oId6oHIJbV4gU_v-Mpi48&gaa_ts=6935bd94&ga
+a_sig=3tbJ_7ZRrguPYjDxogTwd2ytCG7b70pNDaNogjgUg14icaShrFItmMWpypVli_jwY
+m1WncqFLHFta52UOD1ngQ%3D%3D', 'snippet': 'Key Stock Data · P/E Ratio
+(TTM). 37.37(12/05/25) · EPS (TTM). .46 · Market Cap. .12 T · Shares
+Outstanding. 14.78 B · Public Float. 14.76 B · Yield. 0.37%( ...',
+'position': 7}, {'title': 'Apple', 'link':
+'https://www.linkedin.com/company/apple', 'snippet': 'Company size:
+10,001+ employees. Headquarters: Cupertino, California. Type: Public
+Company. Founded: 1976. Specialties: Innovative Product ...',
+'position': 8}, {'title': 'Apple | AAPL Stock Price, Company Overview &
+News', 'link': 'https://www.forbes.com/companies/apple/', 'snippet':
+'Apple Inc. engages in the design, manufacture, and sale of
+smartphones, personal computers, tablets, wearables and accessories,
+and other variety of related ...', 'position': 9}, {'title': 'iCloud',
+'link': 'https://www.icloud.com/', 'snippet': 'Log in to iCloud to
+access your photos, mail, notes, documents and more. Sign in with your
+Apple Account or create a new account to start using Apple services
+...', 'position': 10}], 'credits': 1}
+===============================================================================
+"""
+
serpapi_agent = ChatAgent(
system_message="""You are a helpful assistant that helps users with
their queries""",
diff --git a/test/toolkits/test_search_functions.py b/test/toolkits/test_search_functions.py
index 2a650876cf..596ecf9578 100644
--- a/test/toolkits/test_search_functions.py
+++ b/test/toolkits/test_search_functions.py
@@ -899,3 +899,63 @@ def test_search_metaso_invalid_json(mock_https_connection, search_toolkit):
# Verify connection was attempted
mock_https_connection.assert_called_once_with("metaso.cn")
mock_conn.request.assert_called_once()
+
+
+@patch('requests.post')
+def test_search_serper_success(mock_post, search_toolkit):
+ """Test successful Serper search."""
+ import json
+
+ mock_response = MagicMock()
+ mock_response.status_code = 200
+ mock_response.json.return_value = {
+ "searchParameters": {
+ "q": "apple inc",
+ "gl": "us",
+ "hl": "en",
+ "num": 10,
+ "type": "search",
+ },
+ "organic": [
+ {
+ "title": "Apple",
+ "link": "https://www.apple.com/",
+ "snippet": "Discover the innovative world of Apple...",
+ "position": 1,
+ }
+ ],
+ }
+ mock_post.return_value = mock_response
+
+ with patch.dict(os.environ, {'SERPER_API_KEY': 'test_key'}):
+ result = search_toolkit.search_serper(query="apple inc")
+
+ assert result == mock_response.json.return_value
+
+ # Verify request
+ mock_post.assert_called_once()
+ args, kwargs = mock_post.call_args
+ assert args[0] == "https://google.serper.dev/search"
+ assert kwargs['headers'] == {
+ "X-API-KEY": "test_key",
+ "Content-Type": "application/json",
+ }
+ # Verify payload structure matches exactly what user requested
+ # Note: requests.post(json=payload) sets data to json dump
+ assert json.loads(kwargs['data']) == {
+ "q": "apple inc",
+ "location": "United States",
+ "page": 1,
+ }
+
+
+@patch('requests.post')
+def test_search_serper_error(mock_post, search_toolkit):
+ """Test error handling in Serper search."""
+ mock_post.side_effect = requests.exceptions.RequestException("API Error")
+
+ with patch.dict(os.environ, {'SERPER_API_KEY': 'test_key'}):
+ with pytest.raises(RuntimeError) as excinfo:
+ search_toolkit.search_serper(query="test")
+
+ assert "Error making request to Serper" in str(excinfo.value)