Skip to main content
Amazon Bedrock AgentCore Browser enables agents to interact with web pages through a managed Chrome browser. Agents can navigate websites, extract content, fill forms, click elements, and take screenshots in a secure, managed environment.

Overview

Integration details

ClassPackageSerializableJS supportVersion
BrowserToolkitlangchain-awsβœ…βŒPyPI - Version

Tool features

Returns artifactNative asyncSupports browser interactionPricing
βœ…βœ…βœ…Pay-per-use (AWS)

Available tools

The toolkit provides multiple tools for browser automation:
ToolDescription
navigate_browserNavigate to a URL
click_elementClick on an element using CSS selector
type_textType text into an input field
extract_textExtract all text content from the page
extract_hyperlinksExtract all hyperlinks from the page
get_elementsGet elements matching a CSS selector
current_webpageGet the current page URL and title
navigate_backGo back to the previous page
take_screenshotTake a screenshot of the page
scroll_pageScroll the page in a direction
wait_for_elementWait for an element to appear

Setup

The integration lives in the langchain-aws package. It also requires playwright and beautifulsoup4 for browser automation and HTML parsing.
pip install -U langchain-aws bedrock-agentcore playwright beautifulsoup4
playwright install chromium

Credentials

You need AWS credentials configured with permissions for Bedrock AgentCore Browser. See the Amazon Bedrock AgentCore documentation for required IAM permissions. It’s also helpful (but not needed) to set up LangSmith for best-in-class observability:
import os

os.environ["LANGSMITH_API_KEY"] = "your-api-key"
os.environ["LANGSMITH_TRACING"] = "true"

Instantiation

The toolkit is created using a factory function:
from langchain_aws.tools import create_browser_toolkit

# Create toolkit and get tools
toolkit, browser_tools = create_browser_toolkit(region="us-west-2")

Invocation

Direct tool usage

Get specific tools and invoke them:
# Get tools by name
tools_by_name = toolkit.get_tools_by_name()

# Navigate to a URL (requires config with thread_id)
config = {"configurable": {"thread_id": "session-123"}}

result = tools_by_name["navigate_browser"].invoke(
    {"url": "https://example.com"},
    config=config
)
print(result)

# Extract text from the page
text = tools_by_name["extract_text"].invoke({}, config=config)
print(text)

Use within an agent

import asyncio
from langchain.agents import create_react_agent
from langchain.chat_models import init_chat_model
from langchain_aws.tools import create_browser_toolkit

async def main():
    # Create toolkit
    toolkit, browser_tools = create_browser_toolkit(region="us-west-2")

    # Initialize chat model
    llm = init_chat_model(
        "us.anthropic.claude-sonnet-4-20250514-v1:0",
        model_provider="bedrock_converse",
    )

    # Create agent with browser tools
    agent = create_react_agent(
        model=llm,
        tools=browser_tools,
    )

    # Create config with thread_id for session isolation
    config = {"configurable": {"thread_id": "research-session"}}

    # Run the agent
    result = await agent.ainvoke(
        {"messages": [{
            "role": "user",
            "content": "Navigate to https://example.com and tell me the main heading"
        }]},
        config=config
    )
    print(result["messages"][-1].content)

    # Clean up when done
    await toolkit.cleanup()

asyncio.run(main())

Thread-based session isolation

The toolkit maintains separate browser sessions for each thread_id. This enables concurrent usage without interference:
# Each thread gets its own browser session
config_user1 = {"configurable": {"thread_id": "user-1"}}
config_user2 = {"configurable": {"thread_id": "user-2"}}

# User 1 navigates to site A
tools_by_name["navigate_browser"].invoke(
    {"url": "https://site-a.com"},
    config=config_user1
)

# User 2 navigates to site B (different browser session)
tools_by_name["navigate_browser"].invoke(
    {"url": "https://site-b.com"},
    config=config_user2
)

Browser actions

config = {"configurable": {"thread_id": "session-123"}}

# Navigate to URL
tools_by_name["navigate_browser"].invoke({"url": "https://example.com"}, config=config)

# Go back
tools_by_name["navigate_back"].invoke({}, config=config)

# Get current page info
current = tools_by_name["current_webpage"].invoke({}, config=config)
print(current)  # URL and title

Interacting with elements

# Click an element
tools_by_name["click_element"].invoke({"selector": "#submit-button"}, config=config)

# Type into an input field
tools_by_name["type_text"].invoke({
    "selector": "input[name='search']",
    "text": "search query"
}, config=config)

# Wait for element to appear
tools_by_name["wait_for_element"].invoke({
    "selector": ".results",
    "timeout": 10000,  # 10 seconds
    "state": "visible"
}, config=config)

Extracting content

# Extract all text
text = tools_by_name["extract_text"].invoke({}, config=config)

# Extract all hyperlinks
links = tools_by_name["extract_hyperlinks"].invoke({}, config=config)

# Get specific elements
elements = tools_by_name["get_elements"].invoke(
    {"selector": "article h2"},
    config=config
)

Screenshots and scrolling

# Take screenshot of visible viewport (returns base64 image)
screenshot = tools_by_name["take_screenshot"].invoke(
    {"capture_type": "viewport"},
    config=config
)

# Take screenshot of entire scrollable page
full_screenshot = tools_by_name["take_screenshot"].invoke(
    {"capture_type": "full_page"},
    config=config
)

# Scroll the page
tools_by_name["scroll_page"].invoke({
    "direction": "down",
    "amount": 500  # pixels
}, config=config)

Session cleanup

Always clean up browser sessions when done to release resources:
# Clean up all browser sessions
await toolkit.cleanup()
Note: While create_browser_toolkit() is synchronous, the cleanup() method is asynchronous and must be awaited.

Concurrency protection

The toolkit includes built-in concurrency protection. Each browser session is tied to a specific thread_id, and attempting to access the same session while it’s already in use will raise a RuntimeError. Use different thread_id values for concurrent operations.
# Good: Different thread IDs for concurrent operations
config_a = {"configurable": {"thread_id": "task-a"}}
config_b = {"configurable": {"thread_id": "task-b"}}

# These can run concurrently without conflicts
await asyncio.gather(
    agent.ainvoke({"messages": [...]}, config=config_a),
    agent.ainvoke({"messages": [...]}, config=config_b),
)

API reference

For detailed documentation of all features and configurations, see:
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.