A minimalistic yet ergonomic Rust SDK for the Serper Google Search API. Built with a focus on type safety, modularity, and developer experience.
- π Type-safe API with comprehensive error handling
- ποΈ Modular architecture with clear separation of concerns
- β‘ Async-first design with concurrent request support
- π§ Flexible configuration with environment variable support
- π§ͺ Comprehensive testing with extensive test coverage
- π Rich documentation with examples and API references
First, sign up at serper.dev to get your free API key. The service offers generous free tier limits.
Add this to your Cargo.toml
:
[dependencies]
serper-sdk = "0.1.0"
tokio = { version = "1.0", features = ["full"] }
Basic usage:
use serper_sdk::{SearchService, SearchQuery};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a search service (API key from SERPER_API_KEY env var)
let api_key = std::env::var("SERPER_API_KEY")
.expect("Please set SERPER_API_KEY environment variable");
let service = SearchService::new(api_key)?;
// Build a search query
let query = SearchQuery::new("Hamze Ghalebi CTO at Remolab".to_string())?
.with_location("Paris".to_string())
.with_page(1);
// Execute the search
let response = service.search(&query).await?;
// Process results
for result in response.organic_results() {
println!("{}: {}", result.title, result.link);
}
Ok(())
}
The SDK is organized into focused modules with clear responsibilities:
core
- Fundamental types and error handlingsearch
- Query construction and response parsinghttp
- Transport layer and HTTP client functionalityconfig
- Configuration management with environment variable supportutils
- Common utilities and validation helpers
For detailed architecture information, see ARCHITECTURE.md.
use serper_sdk::{SearchService, SearchQueryBuilder};
let service = SearchService::new("YOUR_API_KEY".to_string())?;
// Using SearchQueryBuilder
let query = SearchQueryBuilder::new()
.query("Hamze Ghalebi CTO at Remolab")
.location("Paris")
.country("fr")
.language("en")
.page(1)
.num_results(20)
.build()?;
let response = service.search(&query).await?;
let response = service.search_with(|builder| {
builder
.query("Hamze Ghalebi CTO at Remolab")
.location("Paris")
.country("fr")
.page(1)
}).await?;
let queries = vec![
SearchQuery::new("Hamze Ghalebi CTO at Remolab".to_string())?,
SearchQuery::new("Hamze Ghalebi Remolab technology".to_string())?,
SearchQuery::new("Remolab France innovation".to_string())?,
];
// Execute up to 3 searches concurrently
let results = service.search_concurrent(&queries, Some(3)).await?;
for (i, response) in results.iter().enumerate() {
println!("Query {}: {} results", i + 1, response.organic_count());
}
use serper_sdk::{SearchServiceBuilder, config::SdkConfig};
use std::time::Duration;
// Using service builder
// Option 1: Using environment variables (recommended)
let config = SdkConfig::from_env()?;
let service = SearchService::new(config.api_key)?;
// Option 2: Manual configuration
let api_key = std::env::var("SERPER_API_KEY")?;
let service = SearchServiceBuilder::new()
.api_key(api_key)
.timeout(Duration::from_secs(60))
.user_agent("my-app/1.0")
.build()?;
// Using configuration object
let api_key = std::env::var("SERPER_API_KEY")?;
let config = SdkConfig::new(api_key)
.with_timeout(Duration::from_secs(60))
.with_max_concurrent(10)
.with_logging(true);
Set environment variables:
export SERPER_API_KEY="your-api-key"
export SERPER_TIMEOUT_SECS="60"
export SERPER_MAX_CONCURRENT="10"
export SERPER_ENABLE_LOGGING="true"
Then use in your code:
use serper_sdk::config::SdkConfig;
let config = SdkConfig::from_env()?;
// Configuration loaded from environment variables
let response = service.search(&query).await?;
// Direct answers
if let Some(answer_box) = &response.answer_box {
if let Some(answer) = &answer_box.answer {
println!("Direct answer: {}", answer);
}
}
// Knowledge graph
if let Some(kg) = &response.knowledge_graph {
if let Some(title) = &kg.title {
println!("Knowledge graph: {}", title);
}
}
// Organic results
for result in response.organic_results() {
println!("Result: {} ({})",
result.title,
result.domain().unwrap_or("unknown")
);
if result.has_snippet() {
println!(" {}", result.snippet_or_default());
}
}
The SDK provides comprehensive error handling with specific error types:
use serper_sdk::core::SerperError;
match service.search(&query).await {
Ok(response) => {
println!("Success: {} results", response.organic_count());
},
Err(SerperError::InvalidApiKey) => {
println!("Invalid API key provided");
},
Err(SerperError::Api { message }) => {
println!("API error: {}", message);
},
Err(SerperError::Request(e)) => {
println!("Network error: {}", e);
},
Err(SerperError::Json(e)) => {
println!("JSON parsing error: {}", e);
},
Err(e) => {
println!("Other error: {}", e);
}
}
The SDK provides rich response types for different result categories:
pub struct OrganicResult {
pub title: String,
pub link: String,
pub snippet: Option<String>,
pub position: u32,
// ... additional fields
}
pub struct AnswerBox {
pub answer: Option<String>,
pub snippet: Option<String>,
pub title: Option<String>,
pub link: Option<String>,
}
pub struct KnowledgeGraph {
pub title: Option<String>,
pub description: Option<String>,
pub entity_type: Option<String>,
pub website: Option<String>,
// ... additional attributes
}
Field | Type | Default | Description |
---|---|---|---|
api_key |
String |
Required | Serper API key |
base_url |
String |
"https://google.serper.dev" |
API base URL |
timeout |
Duration |
30s |
Request timeout |
max_concurrent_requests |
usize |
5 |
Max concurrent requests |
default_headers |
HashMap<String, String> |
{"Content-Type": "application/json"} |
Default headers |
user_agent |
String |
"serper-sdk/{version}" |
User agent string |
enable_logging |
bool |
false |
Enable request/response logging |
Variable | Description | Default |
---|---|---|
SERPER_API_KEY |
API key (required) | None |
SERPER_BASE_URL |
API base URL | "https://google.serper.dev" |
SERPER_TIMEOUT_SECS |
Timeout in seconds | 30 |
SERPER_MAX_CONCURRENT |
Max concurrent requests | 5 |
SERPER_USER_AGENT |
Custom user agent | "serper-sdk/{version}" |
SERPER_ENABLE_LOGGING |
Enable logging ("true" /"false" ) |
false |
Run the test suite:
# Run all tests
cargo test
# Run specific module tests
cargo test search::
cargo test http::
cargo test core::
# Run with output
cargo test -- --nocapture
# Run integration tests
cargo test --test integration_tests
The SDK includes comprehensive tests:
- 54 total tests across all modules
- Unit tests for individual module functionality
- Integration tests for module interactions
- Edge case tests for error conditions and boundary cases
Generate and view the API documentation:
cargo doc --open
Detailed documentation for each module is available in the docs/modules/
directory:
- Core Module - Fundamental types and error handling
- Search Module - Query construction and response handling
- HTTP Module - Transport layer and HTTP client
- Config Module - Configuration management
- Utils Module - Common utilities and helpers
- Architecture Overview - Complete architecture documentation
- Module Dependencies - Module relationships and dependencies
We welcome contributions! Please see our Contributing Guide for details.
- Clone the repository:
git clone https://github.com/RustSandbox/serper.git
cd serper
- Install dependencies:
cargo build
- Run tests:
cargo test
- Check formatting:
cargo fmt --check
- Run clippy:
cargo clippy -- -D warnings
This project is licensed under the MIT License - see the LICENSE file for details.
See CHANGELOG.md for a detailed history of changes.
- π Primary Documentation - Complete API reference and examples on GitHub Pages
- π docs.rs Mirror - Alternative API reference on docs.rs
- π§ Local Documentation - Generate locally with
cargo doc --open
- π Issue Tracker
- π¬ Discussions
This SDK is created and maintained by Hamze Ghalebi, CTO at Remolab. Remolab is a technology company focused on innovative software solutions and API integrations.
- Serper API - The official Serper Google Search API
- reqwest - The HTTP client used by this SDK
- serde - Serialization framework used for JSON handling