Thanks to visit codestin.com
Credit goes to lib.rs

#stac #geospatial #client #json #json-api

bin+lib stac-client

A friendly, async client for the SpatioTemporal Asset Catalog (STAC) specification, written in Rust

4 releases (2 breaking)

0.3.0 Nov 15, 2025
0.2.1 Nov 6, 2025
0.2.0 Oct 12, 2025
0.1.1 Oct 5, 2025

#165 in HTTP client

MIT/Apache

99KB
1.5K SLoC

STAC Client for Rust

Crates.io Documentation CI Publish Code Coverage License Downloads

A friendly, async client for the SpatioTemporal Asset Catalog (STAC) specification, written in Rust. This library helps you interact with STAC APIs to find and retrieve geospatial data.

It is inspired by Python's pystac-client but designed with Rust's principles of type safety, performance, and idiomatic API design in mind.

For architectural decisions and design rationale, see the docs/ directory.

Project Goals

  • Idiomatic Rust API: Provide an API that feels natural to Rust developers, with explicit error handling and proper use of ownership.
  • Minimal Dependencies: Keep the dependency tree small to ensure fast builds and a lean footprint.
  • Well-Documented: Offer clear and comprehensive documentation for all public APIs.
  • Thoroughly Tested: Maintain a high standard of testing to guarantee stability and correctness.

Features

  • Async First: Built on tokio for non-blocking I/O.
  • Strongly-Typed Models: Rust structs for all major STAC objects (Catalog, Collection, Item, etc.).
  • Powerful Search: A fluent builder pattern for constructing complex search queries.
  • Robust Error Handling: Distinct error types for network, parsing, and API-specific issues.
  • Customizable Client: Bring your own reqwest::Client to configure timeouts, proxies, or custom headers.

Optional Features

The following cargo features can be enabled for additional functionality:

  • pagination: Enables the Client::search_next_page method for easy pagination through search results.
  • resilience: Provides retry policies with exponential backoff, jitter, and configurable timeouts. Use with ClientBuilder.
  • auth: Adds pluggable authentication support with ApiKey, BearerToken, and custom AuthLayer implementations.

Installation

Add stac-client to your Cargo.toml:

[dependencies]
stac-client = "0.1.0"
tokio = { version = "1.0", features = ["full"] }

Quick Start

This example shows how to connect to a STAC API, retrieve the root catalog, and run a simple search.

use stac_client::{Client, SearchBuilder};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // The base URL of the STAC API
    let api_url = "https://planetarycomputer.microsoft.com/api/stac/v1";

    // Create a new client
    let client = Client::new(api_url)?;
    println!("Connected to: {}", client.base_url());

    // Get the root catalog to verify the connection
    let catalog = client.get_catalog().await?;
    println!("Root catalog ID: {}", catalog.id);

    // Build a search query for Sentinel-2 data over a specific location and time
    let search = SearchBuilder::new()
        .collections(vec!["sentinel-2-l2a".to_string()])
        .bbox(vec![-74.0, 40.7, -73.9, 40.8]) // West, South, East, North
        .datetime("2023-01-01T00:00:00Z/2023-01-31T23:59:59Z")
        .limit(10)
        .build();

    // Execute the search
    let results = client.search(&search).await?;
    println!("Found {} items in the search.", results.features.len());

    // Process the resulting items
    for item in results.features {
        println!(
            " - Item '{}' from collection '{}'",
            item.id,
            item.collection.as_deref().unwrap_or("N/A")
        );
        for (asset_key, asset) in item.assets {
            println!("   - Asset '{}': {}", asset_key, asset.href);
        }
    }

    Ok(())
}

API Reference

Basic Operations

The Client provides methods for fetching core STAC objects:

# use stac_client::Client;
# use std::error::Error;
# #[tokio::main]
# async fn main() -> Result<(), Box<dyn Error>> {
# let client = Client::new("https://example.com")?;
// Get the root catalog
let catalog = client.get_catalog().await?;

// Get a list of all collections
let collections = client.get_collections().await?;

// Get a single collection by its ID
let collection = client.get_collection("my-collection-id").await?;

// Get items from a collection
let items = client.get_collection_items("my-collection-id", Some(50)).await?;

// Get a single item by its collection and item ID
let item = client.get_item("my-collection-id", "my-item-id").await?;

// Get the API's conformance classes
let conformance = client.get_conformance().await?;
# Ok(())
# }

The SearchBuilder allows you to construct detailed queries. The client supports both POST /search (default) and GET /search.

# use stac_client::{Client, SearchBuilder, SortDirection};
# use serde_json::json;
# use std::error::Error;
# #[tokio::main]
# async fn main() -> Result<(), Box<dyn Error>> {
# let client = Client::new("https://example.com")?;
let search = SearchBuilder::new()
    .limit(100)
    .bbox(vec![-180.0, -90.0, 180.0, 90.0])
    .datetime("2024-01-01T00:00:00Z/..") // Open-ended interval
    .collections(vec!["collection-a".to_string(), "collection-b".to_string()])
    .ids(vec!["item-1".to_string(), "item-2".to_string()])
    .intersects(json!({
        "type": "Point",
        "coordinates": [-122.4, 37.8]
    }))
    .query("eo:cloud_cover", json!({ "lt": 10 })) // STAC Query Extension
    .sort_by("datetime", SortDirection::Desc)
    .include_fields(vec!["id".to_string(), "properties.datetime".to_string()])
    .build();

// Execute using POST /search
let results_post = client.search(&search).await?;

// Execute using GET /search
let results_get = client.search_get(&search).await?;
# Ok(())
# }

Authentication

The auth feature enables pluggable authentication for protected STAC APIs. Enable it in your Cargo.toml:

[dependencies]
stac-client = { version = "0.1.0", features = ["auth", "resilience"] }

API Key Authentication

use stac_client::{ClientBuilder, auth::ApiKey};

# async fn example() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new("https://api.example.com/stac")
    .auth_layer(ApiKey::new("X-API-Key", "your-secret-key"))
    .build()?;

// All requests now include the X-API-Key header
let catalog = client.get_catalog().await?;
# Ok(())
# }

Bearer Token Authentication

use stac_client::{ClientBuilder, auth::BearerToken};

# async fn example() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new("https://api.example.com/stac")
    .auth_layer(BearerToken::new("your-jwt-token"))
    .build()?;

// All requests include Authorization: Bearer header
let results = client.search(&search_params).await?;
# Ok(())
# }

Multiple Auth Layers

You can chain multiple authentication layers:

use stac_client::{ClientBuilder, auth::{ApiKey, BearerToken}};

# async fn example() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new("https://api.example.com/stac")
    .auth_layer(ApiKey::new("X-API-Key", "key123"))
    .auth_layer(BearerToken::new("token456"))
    .build()?;
# Ok(())
# }

Custom Authentication

Implement the AuthLayer trait for custom authentication schemes:

use stac_client::auth::AuthLayer;

#[derive(Debug)]
struct CustomAuth {
    signature: String,
}

impl AuthLayer for CustomAuth {
    fn apply(&self, req: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
        req.header("X-Signature", &self.signature)
    }
}

For more examples, see examples/authentication.rs.

Error Handling

The library returns a stac_client::Error for failed operations, allowing you to handle different failure modes.

# use stac_client::{Client, Error};
# #[tokio::main]
# async fn main() {
# let client = Client::new("https://example.com").unwrap();
match client.get_catalog().await {
    Ok(catalog) => println!("Success: {}", catalog.id),
    Err(Error::Http(e)) => println!("HTTP request failed: {}", e),
    Err(Error::Json(e)) => println!("Failed to parse JSON response: {}", e),
    Err(Error::Api { status, message }) => {
        println!("API returned an error (status {}): {}", status, message)
    },
    Err(e) => println!("An unexpected error occurred: {}", e),
}
# }

Testing

This project maintains a high level of test coverage. To run the test suite:

cargo test -- --nocapture

Contributing

Contributions are welcome! We have a strict set of development standards to ensure the quality and stability of the crate. Please review:

  • CONTRIBUTING.md - Contribution guidelines and release process
  • AGENTS.md - Detailed policies and best practices

All pull requests must pass formatting, linting, and test coverage checks.

License

This project is licensed under either of the following, at your option:

Dependencies

~9–25MB
~307K SLoC