Skip to main content
Parallel is a real-time web search and content extraction platform designed specifically for LLMs and AI applications.
The ParallelWebSearchTool provides access to Parallel’s Search API, which streamlines the traditional search β†’ scrape β†’ extract pipeline into a single API call, returning structured, LLM-optimized results.

Overview

Integration details

ClassPackageSerializableJS supportPackage latest
ParallelWebSearchToollangchain-parallel❌❌PyPI - Latest version

Tool features

  • Real-time web search: Access current information from the web
  • Structured results: Returns compressed, LLM-optimized excerpts
  • Flexible input: Support for natural language objectives or specific search queries
  • Domain filtering: Include or exclude specific domains with source policy
  • Customizable output: Control number of results (1-40) and excerpt length (min 100 chars)
  • Rich metadata: Optional search timing, result counts, and query information
  • Async support: Full async/await support with proper executor handling
  • Error handling: Comprehensive error handling with detailed error messages

Setup

The integration lives in the langchain-parallel package.
pip install -qU langchain-parallel

Credentials

Head to Parallel to sign up and generate an API key. Once you’ve done this set the PARALLEL_API_KEY environment variable:
import getpass
import os

if not os.environ.get("PARALLEL_API_KEY"):
    os.environ["PARALLEL_API_KEY"] = getpass.getpass("Parallel API key:\n")

Instantiation

Here we show how to instantiate an instance of the ParallelWebSearchTool. The tool can be configured with API key and base URL parameters:
from langchain_parallel import ParallelWebSearchTool

# Basic instantiation - API key from environment
tool = ParallelWebSearchTool()

# With explicit API key and custom base URL
tool = ParallelWebSearchTool(
    api_key="your-api-key",
    base_url="https://api.parallel.ai",  # default value
)

Invocation

Invoke directly with args

You can invoke the tool with either an objective (natural language description) or specific search_queries. The tool supports various configuration options including domain filtering and metadata collection:
# Using specific search queries with advanced options
result = tool.invoke(
    {
        "search_queries": [
            "AI breakthroughs 2024",
            "machine learning advances",
            "generative AI news",
        ],
        "max_results": 8,
        "excerpts": {"max_chars_per_result": 2000},
        "mode": "one-shot",  # Use 'agentic' for token-efficient results
        "source_policy": {
            "include_domains": ["arxiv.org", "nature.com"],
            "exclude_domains": ["reddit.com", "twitter.com"],
        },
        "fetch_policy": {
            "max_age_seconds": 86400,  # Cache content for 1 day
            "timeout_seconds": 60,
        },
        "include_metadata": True,
        "timeout": 120,  # Custom timeout in seconds
    }
)

print(result)
# Using an objective (natural language) with metadata
result = tool.invoke(
    {
        "objective": "What are the latest developments in artificial intelligence in 2024?",
        "max_results": 5,
        "include_metadata": True,  # Include search timing and statistics
    }
)

print(result)

# Example response structure:
# {
#     "search_id": "search_abc123...",
#     "results": [
#         {
#             "url": "https://example.com/ai-news",
#             "title": "Latest AI Developments 2024",
#             "excerpts": [
#                 "Recent breakthrough in transformer architectures...",
#                 "New applications in computer vision..."
#             ]
#         }
#     ],
#     "search_metadata": {
#         "search_duration_seconds": 4.123,
#         "search_timestamp": "2024-01-15T10:30:00",
#         "max_results_requested": 5,
#         "actual_results_returned": 4,
#         "search_id": "search_abc123...",
#         "query_count": 1,
#         "source_policy_applied": false
#     }
# }

Invoke with ToolCall

We can also invoke the tool with a model-generated ToolCall, in which case a ToolMessage will be returned:
# This is usually generated by a model, but we'll create a tool call directly for demo purposes.
model_generated_tool_call = {
    "args": {
        "objective": "Find recent news about climate change initiatives",
        "max_results": 3,
        "source_policy": {"include_domains": ["ipcc.ch", "unfccc.int", "nature.com"]},
        "include_metadata": True,
    },
    "id": "call_123",
    "name": tool.name,  # "parallel_web_search"
    "type": "tool_call",
}

result = tool.invoke(model_generated_tool_call)
print(result)
print(f"Tool name: {tool.name}")  # parallel_web_search
print(f"Tool description: {tool.description}")

Async usage

The tool supports full async/await operations for better performance in async applications:
async def search_async():
    return await tool.ainvoke(
        {
            "objective": "Latest quantum computing breakthroughs",
            "max_results": 5,
            "include_metadata": True,
        }
    )


# Run async search
result = await search_async()
print(result)

Parameter details and validation

The tool performs comprehensive input validation and supports the following parameters:

Required parameters

At least one of the following must be provided:
  • objective: Natural language description (max 5000 characters)
  • search_queries: List of search queries (max 5 queries, 200 chars each)

Optional parameters:

  • max_results: Number of results to return (1-40, default: 10)
  • excerpts: Excerpt settings dict (e.g., {"max_chars_per_result": 1500})
  • mode: Search mode - β€˜one-shot’ for comprehensive results, β€˜agentic’ for token-efficient results
  • source_policy: Domain filtering with include_domains and/or exclude_domains lists
  • fetch_policy: Cache control dict (e.g., {"max_age_seconds": 86400, "timeout_seconds": 60})
  • include_metadata: Include search timing and statistics (default: True)
  • timeout: Request timeout in seconds (optional)

Error handling:

The tool provides detailed error messages for validation failures and API errors.
# Example of comprehensive parameter usage
result = tool.invoke(
    {
        "objective": "Find comprehensive information about renewable energy policies in European countries",
        "max_results": 15,
        "excerpts": {
            "max_chars_per_result": 2500
        },  # Longer excerpts for detailed information
        "mode": "one-shot",  # Comprehensive results
        "source_policy": {
            "include_domains": ["europa.eu", "iea.org", "irena.org"],
            "exclude_domains": ["wikipedia.org", "reddit.com"],
        },
        "fetch_policy": {
            "max_age_seconds": 86400,  # 1 day cache
            "timeout_seconds": 90,
        },
        "include_metadata": True,
        "timeout": 180,  # Extended timeout for comprehensive searches
    }
)

# Access results and metadata
print(f"Found {len(result['results'])} results")
if "search_metadata" in result:
    metadata = result["search_metadata"]
    print(f"Search took {metadata['search_duration_seconds']}s")
    print(f"Source policy applied: {metadata.get('source_policy_applied', False)}")

Chaining

We can use our tool in a chain by first binding it to a tool-calling model and then calling it:
# | output: false
# | echo: false

# !pip install -qU langchain langchain-openai
from langchain.chat_models import init_chat_model

llm = init_chat_model(model="gpt-4o", model_provider="openai")
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig, chain

prompt = ChatPromptTemplate(
    [
        ("system", "You are a helpful assistant."),
        ("human", "{user_input}"),
        ("placeholder", "{messages}"),
    ]
)

# Specifying tool_choice will force the model to call this tool.
llm_with_tools = llm.bind_tools([tool], tool_choice=tool.name)

llm_chain = prompt | llm_with_tools


@chain
def tool_chain(user_input: str, config: RunnableConfig):
    input_ = {"user_input": user_input}
    ai_msg = llm_chain.invoke(input_, config=config)
    tool_msgs = tool.batch(ai_msg.tool_calls, config=config)
    return llm_chain.invoke({**input_, "messages": [ai_msg, *tool_msgs]}, config=config)


tool_chain.invoke("What are the latest breakthrough discoveries in quantum computing?")

Best practices

  • Use specific objectives: More specific objectives lead to better, more targeted results
  • Apply domain filtering: Use source_policy to focus on authoritative sources or exclude unreliable domains
  • Include metadata: Set include_metadata: True for debugging and performance optimization
  • Handle errors gracefully: The tool provides detailed error messages for validation and API failures
  • Use async for performance: Use ainvoke() in async applications for better performance

Response format

The tool returns a structured dictionary with the following format:
{
    "search_id": "search_abc123...",  # Unique search identifier
    "results": [  # List of search results
        {
            "url": "https://example.com/page",
            "title": "Page Title",
            "excerpts": [  # Relevant text excerpts
                "First relevant excerpt...",
                "Second relevant excerpt..."
            ]
        }
    ],
    "search_metadata": {  # Optional metadata (if include_metadata=True)
        "search_duration_seconds": 4.123,
        "search_timestamp": "2024-01-15T10:30:00",
        "max_results_requested": 10,
        "actual_results_returned": 8,
        "search_id": "search_abc123...",
        "query_count": 3,  # Number of queries used
        "queries_used": ["query1", "query2", "query3"],  # If search_queries provided
        "source_policy_applied": true,  # If source_policy was used
        "included_domains": ["nature.com"],  # Domains that were included
        "excluded_domains": ["reddit.com"]   # Domains that were excluded
    }
}

API reference

For detailed documentation of all features and configuration options, head to the ParallelWebSearchTool API reference or the Parallel search reference.
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.