Crates.io | qrz-xml |
lib.rs | qrz-xml |
version | 0.1.4 |
created_at | 2025-05-31 18:40:41.807663+00 |
updated_at | 2025-05-31 19:03:56.519781+00 |
description | A Rust client library for the QRZ.com XML API |
homepage | |
repository | https://github.com/n5bur/qrz-xml |
max_upload_size | |
id | 1696542 |
size | 167,083 |
A safe, async Rust client library for the QRZ.com XML API.
Add this to your Cargo.toml
:
[dependencies]
qrz-xml = "0.1"
tokio = { version = "1.0", features = ["full"] }
use qrz_xml::{QrzXmlClient, ApiVersion};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client using your QRZ.com credentials
let client = QrzXmlClient::new(
"your_username",
"your_password",
ApiVersion::Current
)?;
// Look up a callsign
let callsign_info = client.lookup_callsign("AA7BQ").await?;
println!("Found: {} - {}",
callsign_info.call,
callsign_info.full_name().unwrap_or_default()
);
// Look up DXCC entity
let dxcc_info = client.lookup_dxcc_entity(291).await?;
println!("DXCC 291: {}", dxcc_info.name);
Ok(())
}
You need a valid QRZ.com username and password. While any QRZ user can authenticate, most features require an active QRZ Logbook Data subscription.
Visit QRZ.com subscriptions for more information about subscription plans.
let callsign_info = client.lookup_callsign("W1AW").await?;
// Access comprehensive information
println!("Name: {}", callsign_info.full_name().unwrap_or_default());
println!("Grid: {}", callsign_info.grid.unwrap_or_default());
println!("Country: {}", callsign_info.country.unwrap_or_default());
// Geographic coordinates
if let Some((lat, lon)) = callsign_info.coordinates() {
println!("Location: {:.4}°, {:.4}°", lat, lon);
}
// QSL preferences
if callsign_info.accepts_eqsl() == Some(true) {
println!("Accepts eQSL");
}
// Look up by entity number
let usa = client.lookup_dxcc_entity(291).await?;
println!("{} - {}", usa.cc.unwrap(), usa.name);
// Look up by callsign prefix
let dxcc = client.lookup_dxcc_by_callsign("JA1ABC").await?;
println!("Japan: DXCC {}", dxcc.dxcc);
let bio = client.lookup_biography("AA7BQ").await?;
println!("Biography HTML length: {}", bio.html().len());
// The biography contains raw HTML as it appears on QRZ.com
if !bio.is_empty() {
// Process or display the HTML content
println!("Has biography data available");
}
The library provides comprehensive error handling with specific error types:
use qrz_xml::QrzXmlError;
match client.lookup_callsign("INVALID").await {
Ok(info) => println!("Found: {}", info.call),
Err(QrzXmlError::CallsignNotFound { callsign }) => {
println!("Callsign {} not found", callsign);
}
Err(QrzXmlError::SubscriptionRequired) => {
println!("This feature requires a QRZ subscription");
}
Err(QrzXmlError::AuthenticationFailed { reason }) => {
println!("Auth failed: {}", reason);
}
Err(e) => println!("Other error: {}", e),
}
Customize the client behavior with QrzXmlClientConfig
:
use qrz::{QrzXmlClient, ApiVersion};
use qrz::client::QrzXmlClientConfig
let config = QrzXmlClientConfig {
base_url: "https://xmldata.qrz.com/xml".to_string(),
user_agent: "my-app/1.0".to_string(),
timeout_seconds: 30,
max_retries: 3,
};
let client = QrzXmlClient::with_config(
"username",
"password",
ApiVersion::Current,
config
)?;
QRZ.com provides a versioned XML interface. You can specify which version to use:
// Use the latest version (recommended)
let client = QrzXmlClient::new("user", "pass", ApiVersion::Current)?;
// Use a specific version
let client = QrzXmlClient::new("user", "pass", ApiVersion::version("1.34"))?;
// Use legacy version (1.24)
let client = QrzXmlClient::new("user", "pass", ApiVersion::Legacy)?;
The client automatically handles session management:
// Check authentication status
if client.is_authenticated().await {
println!("Ready to make requests");
}
// Get session information
if let Some((count, sub_exp)) = client.session_info().await {
println!("Lookups today: {:?}", count);
println!("Subscription expires: {:?}", sub_exp);
}
// Force re-authentication if needed
client.reauthenticate().await?;
The library respects QRZ.com's usage guidelines:
You should implement your own rate limiting if making many requests:
use tokio::time::{sleep, Duration};
for callsign in callsigns {
let result = client.lookup_callsign(&callsign).await?;
// Process result...
// Be respectful - add a small delay between requests
sleep(Duration::from_millis(100)).await;
}
The crate includes several examples in the examples/
directory:
# Basic callsign lookup
QRZ_USERNAME=xxx QRZ_PASSWORD=yyy cargo run --example basic_lookup -- AA7BQ
# DXCC entity lookup
QRZ_USERNAME=xxx QRZ_PASSWORD=yyy cargo run --example dxcc_lookup -- --entity 291
Run the test suite:
cargo test
The tests include both unit tests and integration tests with mocked API responses, so they don't require QRZ.com credentials.
The library supports both native TLS and rustls:
# Use native TLS (default)
qrz_xml = "0.1"
# Use rustls instead
qrz_xml = { version = "0.1", default-features = false, features = ["rustls-tls"] }
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under either of
at your option.
This library is not affiliated with or endorsed by QRZ.com. QRZ.com is a trademark of QRZ LLC.
Users of this library must comply with QRZ.com's Terms of Service and API usage guidelines.