Official Rust SDK for the XposedOrNot API
Check if your email or password has been exposed in data breaches
Note: This SDK uses the free public API from XposedOrNot.com -- a free service to check if your email has been compromised in data breaches. Visit the XposedOrNot website to learn more about the service and check your email manually.
- Features
- Installation
- Requirements
- Quick Start
- API Reference
- Error Handling
- Rate Limits
- Configuration
- Contributing
- License
- Links
- Email Breach Check -- Query the free or Plus API for email exposures
- Breach Listing -- List all known breaches with optional domain filtering
- Breach Analytics -- Detailed summaries, metrics, and paste data for an email
- Password Check -- k-anonymity via Keccak-512; the full password never leaves the client
- Async/Await -- Built on
reqwestandtokiofor non-blocking I/O - Rate Limiting -- Automatic client-side throttling (1 req/sec for the free API)
- Retry Logic -- Exponential backoff on HTTP 429 responses
- Configurable -- Builder pattern for timeout, retries, custom headers, and API key
cargo add xposedornotOr add it directly to your Cargo.toml:
[dependencies]
xposedornot = "1.0"- Rust 1.70 or higher
- A tokio async runtime (the crate depends on
tokiofor rate limiting and timing)
use xposedornot::Client;
#[tokio::main]
async fn main() -> Result<(), xposedornot::Error> {
let client = Client::builder().build()?;
// Check if an email has been breached
let result = client.check_email("test@example.com").await?;
println!("{:?}", result);
// Check password exposure (password is hashed locally)
let pw = client.check_password("password123").await?;
println!("Seen {} times", pw.search_pass_anon.count);
Ok(())
}use xposedornot::Client;
let client = Client::builder()
.timeout_secs(60)
.max_retries(5)
.build()?;See Configuration for all builder options.
Check if an email address has been exposed in any data breaches.
When the client has an API key, the Plus API is used and returns detailed breach records. Without an API key the free API returns a simple list of breach names.
// Free API
let client = Client::builder().build()?;
let result = client.check_email("user@example.com").await?;
println!("{:?}", result);
// Plus API -- returns detailed breach records
let client = Client::builder()
.api_key("your-api-key")
.build()?;
let result = client.check_email("user@example.com").await?;
println!("{:?}", result);Returns: EmailCheckResult -- an enum with Free(FreeEmailCheckResponse) or Plus(PlusEmailCheckResponse) variants depending on whether an API key is configured.
List all known data breaches, optionally filtered by domain.
// Get all breaches
let all = client.get_breaches(None).await?;
// Filter by domain
let filtered = client.get_breaches(Some("adobe.com")).await?;
for breach in &filtered.exposed_breaches {
println!("{} - {} records", breach.breach_id, breach.exposed_records);
}Returns: BreachListResponse containing a Vec<BreachRecord> with fields:
breach_id-- Unique identifierbreached_date-- Date of the breachdomain-- Associated domainindustry-- Industry categoryexposed_data-- Types of data exposedexposed_records-- Number of records exposedverified-- Whether the breach is verified
Get detailed breach analytics for an email address, including breach summaries, metrics, and paste exposures.
let analytics = client.breach_analytics("user@example.com").await?;
println!("Breaches: {:?}", analytics.exposed_breaches);
println!("Summary: {:?}", analytics.breaches_summary);
println!("Metrics: {:?}", analytics.breach_metrics);
println!("Pastes: {:?}", analytics.exposed_pastes);Returns: BreachAnalyticsResponse with fields:
exposed_breaches-- Breach detailsbreaches_summary-- Aggregated summarybreach_metrics-- Analytical metricspastes_summary-- Paste summaryexposed_pastes-- List of paste exposures
Check if a password has been seen in known breaches. The password is hashed locally using Keccak-512 and only the first 10 hex characters of the digest are sent to the API (k-anonymity). The full password never leaves the client.
let result = client.check_password("mysecretpassword").await?;
println!("Exposure count: {}", result.search_pass_anon.count);Returns: PasswordCheckResponse with a search_pass_anon field containing:
anon-- The anonymous hash portionchar-- Character composition breakdown (e.g.,"D:3;A:8;S:0;L:11")count-- Number of times the password has been seen
All methods return Result<T, xposedornot::Error>. Use pattern matching on the Error enum to handle specific failure modes:
use xposedornot::{Client, Error};
let client = Client::builder().build()?;
match client.check_email("user@example.com").await {
Ok(result) => println!("{:?}", result),
Err(Error::Validation { message }) => {
eprintln!("Invalid input: {message}");
}
Err(Error::RateLimit { message }) => {
eprintln!("Rate limited: {message}");
}
Err(Error::NotFound { message }) => {
eprintln!("Not found: {message}");
}
Err(Error::Authentication { message }) => {
eprintln!("Auth failed: {message}");
}
Err(Error::Network { source }) => {
eprintln!("Network error: {source}");
}
Err(Error::Api { status_code, message }) => {
eprintln!("API error ({status_code}): {message}");
}
}| Variant | Description |
|---|---|
Validation |
Invalid input (e.g., malformed email, empty password) |
RateLimit |
API rate limit exceeded (HTTP 429) |
NotFound |
Resource not found (HTTP 404) |
Authentication |
Invalid or missing API key (HTTP 401/403) |
Network |
Connection or transport error (wraps reqwest::Error) |
Api |
Unexpected API response or server error |
The XposedOrNot free API has the following rate limits:
- 2 requests per second
- 50-100 requests per hour
- 100-1000 requests per day
The client enforces client-side rate limiting at 1 request per second for the free API. When an API key is configured (Plus API), client-side throttling is disabled.
Server-side 429 responses are retried automatically with exponential backoff (1s, 2s, 4s, ...) up to max_retries attempts.
Use the builder pattern to customize the client:
use xposedornot::Client;
let client = Client::builder()
.api_key("your-api-key") // Enable Plus API
.timeout_secs(60) // Request timeout (default: 30s)
.max_retries(5) // Retry attempts on 429 (default: 3)
.base_url("https://custom.api") // Override free API base URL
.header("X-Custom", "value") // Add custom headers
.build()?;| Method | Default | Description |
|---|---|---|
api_key(key) |
None |
API key for Plus API; disables client-side rate limiting |
timeout_secs(secs) |
30 |
Request timeout in seconds |
max_retries(n) |
3 |
Max retry attempts on HTTP 429 |
base_url(url) |
https://api.xposedornot.com |
Free API base URL |
plus_base_url(url) |
https://plus-api.xposedornot.com |
Plus API base URL |
password_base_url(url) |
https://passwords.xposedornot.com/api |
Password API base URL |
header(name, value) |
-- | Add a custom header to every request |
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Clone the repository
git clone https://github.com/XposedOrNot/XposedOrNot-Rust.git
cd XposedOrNot-Rust
# Build
cargo build
# Run tests
cargo test
# Run clippy
cargo clippy -- -D warnings
# Format
cargo fmt --checkMIT -- see the LICENSE file for details.
- XposedOrNot Website
- API Documentation
- crates.io Package
- docs.rs Documentation
- GitHub Repository
- XposedOrNot API Repository
Made with care by XposedOrNot