brave_search v0.1.0
Brave Search Crystal Library
A Crystal library for the Brave Search API with full type safety, comprehensive endpoint support, and excellent developer experience.
Features
- Complete API Coverage: Web, News, Image, Video, Suggest, Spellcheck, Local POI, Local Descriptions, Rich Results, and Summarizer endpoints
- Type Safety: Leverages Crystal's static typing with enums for parameters like
SafeSearch,Freshness, andUnits - Robust HTTP Handling: Configurable timeouts, automatic retries, and gzip/deflate decompression
- Comprehensive Error Handling: Typed exceptions for authentication, rate limiting, validation, and timeout errors
- Testable: HTTP adapter abstraction enables testing without live API calls
Installation
Add the dependency to your shard.yml:
dependencies:
brave_search:
github: amscotti/brave_search
Then run:
shards install
Quick Start
require "brave_search"
client = BraveSearch::Client.new(ENV["BRAVE_API_KEY"])
# Simple web search
results = client.web_search("crystal programming language")
results.web_results.each do |result|
puts "#{result.title}: #{result.url}"
end
Usage Examples
Web Search with Options
results = client.web_search("crystal programming",
count: 10,
safesearch: BraveSearch::Types::SafeSearch::Moderate,
freshness: BraveSearch::Types::Freshness::PastWeek,
units: BraveSearch::Types::Units::Metric,
extra_snippets: true
)
# Custom date range
range = BraveSearch::Types::FreshnessRange.new(
Time.utc(2024, 1, 1),
Time.utc(2024, 6, 30)
)
results = client.web_search("crystal news", freshness: range)
News Search
results = client.news_search("technology news", count: 5)
results.results.each do |article|
puts "#{article.title} (#{article.age})"
puts " Source: #{article.source.try(&.name)}"
puts " Breaking: #{article.breaking}"
end
Image Search
results = client.image_search("golden gate bridge", count: 20)
results.results.each do |image|
puts "#{image.title}"
puts " Thumbnail: #{image.thumbnail.try(&.src)}"
puts " Size: #{image.properties.try(&.width)}x#{image.properties.try(&.height)}"
end
Video Search
results = client.video_search("crystal tutorial")
results.results.each do |video|
puts "#{video.title}"
puts " Duration: #{video.video.try(&.duration)}"
puts " Views: #{video.video.try(&.views)}"
end
Search Suggestions
results = client.suggest("crystal lang", rich: true)
results.results.each do |suggestion|
puts suggestion.query
end
Spellcheck
results = client.spellcheck("progrming")
results.results.each do |correction|
puts "Did you mean: #{correction.query}"
end
Local Search (Two-Step Process)
# Step 1: Get location IDs from web search
web_results = client.web_search("coffee shops near me")
location_ids = web_results.locations.try(&.results.map(&.id)) || [] of String
# Step 2: Get detailed POI info
if location_ids.any?
pois = client.local_pois(location_ids)
pois.results.each do |poi|
puts "#{poi.name}"
puts " Rating: #{poi.rating.try(&.rating_value)}/5"
puts " Address: #{poi.address.try(&.display_address)}"
puts " Price: #{poi.price_range}"
end
# Get AI-generated descriptions
descriptions = client.local_descriptions(location_ids)
descriptions.results.each do |desc|
puts "#{desc.id}: #{desc.description}"
end
end
Summarizer
# Enable summary in web search
web_results = client.web_search("what is crystal programming language", summary: true)
if summary_key = web_results.query.summary_key
summary = client.summarizer(summary_key)
puts "Title: #{summary.title}"
summary.summary.try(&.each do |message|
puts message.data if message.type == "text"
end)
puts "\nFollow-up questions:"
summary.followups.try(&.each { |q| puts " - #{q}" })
end
Configuration
config = BraveSearch::Config.new
config.timeout = 60.seconds # HTTP request timeout (default: 30s)
config.retry_count = 5 # Number of retries on network errors (default: 3)
config.retry_delay = 2.seconds # Delay between retries (default: 1s)
config.poll_interval = 1.second # Summarizer polling interval (default: 500ms)
config.max_poll_attempts = 30 # Max summarizer poll attempts (default: 20)
client = BraveSearch::Client.new(ENV["BRAVE_API_KEY"], config: config)
Error Handling
begin
results = client.web_search("test")
rescue BraveSearch::ValidationError => e
puts "Invalid parameter '#{e.field}': #{e.message}"
rescue BraveSearch::AuthenticationError
puts "Invalid API key"
rescue BraveSearch::RateLimitError => e
if retry_after = e.retry_after
puts "Rate limited. Retry after #{retry_after.total_seconds} seconds"
end
rescue BraveSearch::TimeoutError
puts "Request timed out"
rescue BraveSearch::HTTPError => e
puts "HTTP error #{e.status}: #{e.body}"
rescue BraveSearch::APIError => e
puts "API error: #{e.message}"
end
Type-Safe Enums
The library provides enums for type-safe parameter values:
# SafeSearch levels
BraveSearch::Types::SafeSearch::Off
BraveSearch::Types::SafeSearch::Moderate
BraveSearch::Types::SafeSearch::Strict
# Freshness options
BraveSearch::Types::Freshness::PastDay # pd
BraveSearch::Types::Freshness::PastWeek # pw
BraveSearch::Types::Freshness::PastMonth # pm
BraveSearch::Types::Freshness::PastYear # py
# Units
BraveSearch::Types::Units::Metric
BraveSearch::Types::Units::Imperial
Testing
The library uses an HTTP adapter pattern for testability:
require "brave_search"
require "brave_search/http/mock_adapter"
adapter = BraveSearch::HTTP::MockAdapter.new
adapter.stub_json("/web/search", %({"type": "search", ...}))
client = BraveSearch::Client.new("test-key", adapter)
result = client.web_search("test")
# Verify requests
adapter.requests.first.headers["X-Subscription-Token"].should eq("test-key")
API Rate Limits
Rate limits vary by plan:
| Plan | Requests/Second | Requests/Month |
|---|---|---|
| Free | 1 | 2,000 |
| Base | 1 | 20,000 |
| Pro | 20 | Unlimited |
Development
# Install dependencies
shards install
# Run tests
crystal spec
# Run linter
./bin/ameba
# Format code
crystal tool format
Contributing
- Fork it (https://github.com/amscotti/brave_search/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
License
MIT License - see LICENSE for details.
References
Repository
brave_search
Owner
Statistic
- 0
- 0
- 0
- 0
- 1
- about 7 hours ago
- January 17, 2026
License
MIT License
Links
Synced at
Sun, 18 Jan 2026 00:43:34 GMT
Languages