Skip to content

BraveWebSearchTool

Web search capabilities using the Brave Search API. Provides up-to-date information from the web with optional domain filtering.

Features: - Search the web using Brave's privacy-focused search API - Configurable result count (default: 10) - Domain filtering (include/exclude specific domains) - Supports both web and video search results - Client-side domain filtering with subdomain matching - Structured JSON response format - Error handling with graceful fallbacks

Overview

The BraveWebSearchTool enables AI agents to search the web for current information beyond their knowledge cutoff. It uses the Brave Search API to retrieve search results and returns them in a structured format suitable for AI processing.

Key Characteristics: - Returns search results as JSON with title, URL, and description - Supports domain-based filtering (client-side) - Includes both web pages and videos in results - Privacy-focused (Brave Search doesn't track users)

Basic Usage

BraveWebSearchTool searchTool = BraveWebSearchTool.builder("your-api-key")
    .build();

String results = searchTool.webSearch("Spring AI framework", null, null);
System.out.println(results);

Response Format:

[
  {
    "title": "Spring AI - Spring",
    "url": "https://spring.io/projects/spring-ai",
    "description": "Spring AI provides abstractions for developing AI applications..."
  },
  {
    "title": "Spring AI Reference Documentation",
    "url": "https://docs.spring.io/spring-ai/reference/",
    "description": "Comprehensive guide to Spring AI features..."
  }
]

Getting an API Key

  1. Visit Brave Search API
  2. Sign up for an account
  3. Subscribe to a plan (Free tier available with rate limits)
  4. Copy your API subscription token
  5. Store it securely (environment variable recommended)

Pricing (as of 2025): - Free: 2,000 queries/month - Paid plans: Higher limits and additional features

Builder Configuration

Required Parameter

apiKey - Your Brave Search API subscription token

BraveWebSearchTool.builder("your-api-key-here")
    .build();

Optional Parameters

Option Default Description
resultCount 10 Number of search results to return (max typically 20)

Example with custom result count:

BraveWebSearchTool searchTool = BraveWebSearchTool.builder("your-api-key")
    .resultCount(15)  // Return up to 15 results
    .build();

Domain Filtering

Client-Side Filtering

The tool supports filtering results by domain. Important: Domain filtering is applied client-side after fetching results, meaning filtered results still count against your API quota.

Include Specific Domains

BraveWebSearchTool searchTool = BraveWebSearchTool.builder("your-api-key").build();

// Only include results from spring.io
String results = searchTool.webSearch(
    "Spring Boot tutorial",
    List.of("spring.io"),  // allowedDomains
    null                   // blockedDomains
);

Exclude Specific Domains

// Exclude results from example.com and spam.com
String results = searchTool.webSearch(
    "Java programming",
    null,                              // allowedDomains
    List.of("example.com", "spam.com") // blockedDomains
);

Combine Allowed and Blocked

// Only spring.io and baeldung.com, but exclude outdated-site.com
String results = searchTool.webSearch(
    "Spring Framework 2025",
    List.of("spring.io", "baeldung.com"),  // allowedDomains
    List.of("outdated-site.com")           // blockedDomains
);

Subdomain Matching

Domain filters automatically match subdomains:

// Filter: "spring.io" matches:
// - spring.io
// - docs.spring.io
// - start.spring.io
// - blog.spring.io

For better API quota efficiency, use Brave's search operators directly in your query instead of client-side filtering:

BraveWebSearchTool searchTool = BraveWebSearchTool.builder("your-api-key").build();

// Search only on spring.io (more efficient)
String results = searchTool.webSearch("Spring AI site:spring.io", null, null);

// Exclude a domain
String results2 = searchTool.webSearch("Java tutorial -site:example.com", null, null);

// Multiple site operators
String results3 = searchTool.webSearch(
    "Spring Framework (site:spring.io OR site:baeldung.com)",
    null,
    null
);

Why search operators are better: - Results are filtered server-side by Brave - Doesn't waste API quota on filtered-out results - Faster response (no client-side processing) - More accurate (Brave's native filtering)

Spring Boot Integration

Configuration with Environment Variable

application.properties:

brave.api.key=${BRAVE_API_KEY}

Environment variable:

export BRAVE_API_KEY="your-api-key-here"

Bean Configuration

@Configuration
public class SearchConfig {

    @Bean
    public BraveWebSearchTool braveWebSearchTool(
            @Value("${brave.api.key}") String apiKey) {
        return BraveWebSearchTool.builder(apiKey)
            .resultCount(10)
            .build();
    }
}

ChatClient Integration

@Configuration
public class AgentConfig {

    @Bean
    public ChatClient chatClient(
            ChatClient.Builder chatClientBuilder,
            @Value("${brave.api.key}") String braveApiKey) {

        return chatClientBuilder
            .defaultTools(BraveWebSearchTool.builder(braveApiKey)
                .resultCount(15)
                .build())
            .build();
    }
}

Usage Examples

BraveWebSearchTool searchTool = BraveWebSearchTool.builder("api-key").build();

String results = searchTool.webSearch("latest Spring AI features 2025", null, null);
// Returns JSON array of search results

Example 2: Current Events

// AI automatically uses current year in query
String results = searchTool.webSearch(
    "Spring Framework news 2025",
    null,
    null
);

Example 3: Domain-Specific Research

// Research from official documentation only
String officialDocs = searchTool.webSearch(
    "Spring AI vector stores",
    List.of("spring.io", "docs.spring.io"),
    null
);

// Exclude tutorial sites, focus on official sources
String research = searchTool.webSearch(
    "Spring Boot 4.0 migration guide",
    List.of("spring.io"),
    List.of("tutorialspoint.com", "w3schools.com")
);

Example 4: Competitive Research

// Compare frameworks, exclude specific domains
String comparison = searchTool.webSearch(
    "Spring vs Quarkus performance benchmarks",
    null,
    List.of("biased-site.com")
);

Example 5: Technology Stack Research

// Find tutorials from trusted sources
String tutorials = searchTool.webSearch(
    "Spring AI LangChain4j integration tutorial",
    List.of("spring.io", "github.com", "medium.com", "dev.to"),
    null
);

AI Agent Integration

When integrated with ChatClient, AI agents can automatically search the web and cite sources:

ChatClient chatClient = chatClientBuilder
    .defaultTools(BraveWebSearchTool.builder(apiKey).build())
    .build();

String response = chatClient.prompt()
    .user("What are the latest Spring AI features announced in 2025?")
    .call()
    .content();

// AI response will include search results and sources:
// "Based on recent announcements, Spring AI 2.0 includes..."
//
// Sources:
// - [Spring AI 2.0 Release Notes](https://spring.io/blog/2025/...)
// - [Spring AI Documentation](https://docs.spring.io/spring-ai/...)

Response Format

The tool returns a JSON array of search results:

[
  {
    "title": "Page Title",
    "url": "https://example.com/page",
    "description": "Brief description or snippet from the page"
  }
]

Fields: - title (string): The title of the web page or video - url (string): The full URL - description (string): Snippet or summary (may be empty for some results)

Error Handling

Empty Query

String results = searchTool.webSearch("", null, null);
// Returns: "[]" (empty JSON array)

API Errors

// Invalid API key, network error, or API down
String results = searchTool.webSearch("query", null, null);
// Returns: "[]" (empty JSON array)
// Error logged but doesn't throw exception

Null/Invalid API Key

// Throws IllegalArgumentException at build time
BraveWebSearchTool searchTool = BraveWebSearchTool.builder(null).build();
// Exception: "API key must not be null or empty"

Best Practices

1. Use Search Operators for Filtering

// ✅ GOOD: Server-side filtering (efficient)
searchTool.webSearch("Spring AI site:spring.io", null, null);

// ❌ LESS EFFICIENT: Client-side filtering (wastes quota)
searchTool.webSearch("Spring AI", List.of("spring.io"), null);

2. Include Year in Queries

// ✅ GOOD: Specifies current year for recent info
searchTool.webSearch("React best practices 2025", null, null);

// ❌ LESS PRECISE: May return outdated results
searchTool.webSearch("React best practices", null, null);

3. Store API Key Securely

// ✅ GOOD: Environment variable
String apiKey = System.getenv("BRAVE_API_KEY");

// ✅ GOOD: Spring property
@Value("${brave.api.key}") String apiKey;

// ❌ BAD: Hardcoded
String apiKey = "abc123..."; // Never do this!

4. Optimize Result Count

// ✅ GOOD: Just enough results
BraveWebSearchTool.builder(apiKey).resultCount(10).build();

// ❌ WASTEFUL: Too many results (costs more quota)
BraveWebSearchTool.builder(apiKey).resultCount(50).build();

5. Handle Empty Results

String jsonResults = searchTool.webSearch("query", null, null);

if (jsonResults.equals("[]")) {
    // No results found or error occurred
    logger.warn("Search returned no results");
}

Advanced Usage

Custom Result Processing

BraveWebSearchTool searchTool = BraveWebSearchTool.builder(apiKey).build();

String jsonResults = searchTool.webSearch("Spring AI", null, null);

// Parse JSON and process
ObjectMapper mapper = new ObjectMapper();
List<SearchResult> results = mapper.readValue(
    jsonResults,
    new TypeReference<List<SearchResult>>() {}
);

for (SearchResult result : results) {
    System.out.println(result.title() + ": " + result.url());
}

Combining with SmartWebFetchTool

BraveWebSearchTool searchTool = BraveWebSearchTool.builder(apiKey).build();
SmartWebFetchTool fetchTool = SmartWebFetchTool.builder(chatClient).build();

// 1. Search for relevant pages
String searchResults = searchTool.webSearch(
    "Spring AI vector databases tutorial",
    List.of("spring.io", "baeldung.com"),
    null
);

// 2. Parse results
List<SearchResult> results = parseResults(searchResults);

// 3. Fetch and summarize top result
if (!results.isEmpty()) {
    String url = results.get(0).url();
    String summary = fetchTool.webFetch(url, "Summarize the tutorial");
    System.out.println(summary);
}

Multi-Source Research

BraveWebSearchTool searchTool = BraveWebSearchTool.builder(apiKey)
    .resultCount(20)
    .build();

String[] queries = {
    "Spring AI embeddings site:spring.io",
    "Spring AI vector store comparison",
    "Spring AI RAG implementation guide"
};

for (String query : queries) {
    String results = searchTool.webSearch(query, null, null);
    // Process each result set
    analyzeResults(results);
}

Limitations

API Quota

  • Free tier: 2,000 queries/month
  • Each query counts even if results are filtered client-side
  • Use search operators for efficient filtering

Geographic Availability

  • Web search is optimized for US-based queries
  • Results may vary by geographic location
  • Some features may be region-specific

Client-Side Filtering

  • Domain filtering happens after API call
  • Filtered results still count against quota
  • Use site: operator for server-side filtering

Result Count Limits

  • Maximum typically ~20 results per query
  • Higher counts may not return proportionally more results
  • Brave API limits may apply

No Authentication Support

  • Results are from public web only
  • Cannot access authenticated/paywalled content
  • No support for API keys in search results

Troubleshooting

"Empty response from Brave Search API"

Causes: - Invalid API key - Network connectivity issues - API service down - Rate limit exceeded

Solutions:

// Verify API key is correct
String apiKey = System.getenv("BRAVE_API_KEY");
logger.info("Using API key: {}...", apiKey.substring(0, 8));

// Check API status at brave.com

Results Count Against Quota

Problem: Domain filtering still uses quota

Solution: Use search operators instead

// Instead of this:
searchTool.webSearch("query", List.of("example.com"), null);

// Do this:
searchTool.webSearch("query site:example.com", null, null);

No Results Returned

Causes: - Query too specific - All results filtered by domain rules - Brave has no indexed results

Solutions:

// Broaden the query
searchTool.webSearch("Spring Framework", null, null);

// Relax domain filters
searchTool.webSearch("query", null, null);

// Check if results exist on Brave Search directly

Rate Limit Exceeded

Solution: Implement rate limiting

@Component
public class RateLimitedSearchTool {
    private final BraveWebSearchTool searchTool;
    private final RateLimiter rateLimiter;

    public RateLimitedSearchTool(BraveWebSearchTool searchTool) {
        this.searchTool = searchTool;
        // 2000 queries per month = ~1.5 per minute average
        this.rateLimiter = RateLimiter.create(1.0);
    }

    public String search(String query) {
        rateLimiter.acquire();
        return searchTool.webSearch(query, null, null);
    }
}

Security Considerations

API Key Protection

Never commit API keys to version control:

# .gitignore
.env
application-local.properties

Use environment variables:

export BRAVE_API_KEY="your-key"

Or Spring profiles:

# application-local.properties (not committed)
brave.api.key=your-actual-key

Input Validation

The tool automatically handles: - Empty queries (returns empty array) - Null domain lists (no filtering applied) - Invalid URLs in results (filtered out)

Query Sanitization

// Tool automatically handles special characters
String results = searchTool.webSearch(
    "user input with special chars: & ? #",
    null,
    null
);
// URL encoding handled automatically

Performance Tips

Minimize API Calls

// ✅ GOOD: Single search with site operator
searchTool.webSearch("Spring AI site:spring.io OR site:baeldung.com", null, null);

// ❌ WASTEFUL: Multiple searches
searchTool.webSearch("Spring AI site:spring.io", null, null);
searchTool.webSearch("Spring AI site:baeldung.com", null, null);

Cache Results

@Component
public class CachedSearchTool {
    private final BraveWebSearchTool searchTool;
    private final Cache<String, String> cache;

    public CachedSearchTool(BraveWebSearchTool searchTool) {
        this.searchTool = searchTool;
        this.cache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(15, TimeUnit.MINUTES)
            .build();
    }

    public String search(String query) {
        return cache.get(query, () -> searchTool.webSearch(query, null, null));
    }
}

Optimize Result Count

// Start with fewer results
BraveWebSearchTool searchTool = BraveWebSearchTool.builder(apiKey)
    .resultCount(5)  // Only fetch what you need
    .build();

Source Citation Requirements

CRITICAL: When using search results in AI responses, always cite sources:

// AI must include sources section
String response = """
    Based on the search results, Spring AI 2.0 introduces vector store support...

    Sources:
    - [Spring AI Release Notes](https://spring.io/blog/2025/01/spring-ai-2.0)
    - [Vector Store Documentation](https://docs.spring.io/spring-ai/reference/vectors)
    """;

Required format: - "Sources:" section at the end of response - Each source as markdown link: [Title](URL) - Include all relevant sources used

See Also