spoo.me Rust SDK
The official Rust SDK for the spoo.me link management API.
# async
- Async-first on
reqwestwith rustls; wasm32 supported (compile-checked and runtime-tested in a headless browser in CI) - Typed errors, automatic retries, streaming pagination and exports
- Timestamps in and out as
chrono::DateTime<Utc>, whatever the wire format - Anonymous, API key, and Sign in with Spoo authentication
#![forbid(unsafe_code)], panic-free library paths, thin dependency tree
Install
Requires Rust 1.85 or newer. The command installs the SDK only; snippets on
this page also use chrono (timestamps in the public types), and some use
reqwest (client injection), futures-util (stream combinators) or serde
with the derive feature (escape-hatch response types). Add the ones your
code names.
Authentication
Create an API key from your spoo.me dashboard and pass it explicitly:
#
Client::from_env() reads SPOO_API_KEY for you; that constructor is the
only place the crate touches the environment. Client::anonymous() works
without an account: anonymous shortening and the public endpoints (stats,
previews, the emoji set) need no credentials.
Self-hosting spoo.me, sharing a connection pool, or tagging your app:
#
The client is Send + Sync + Clone and cheap to clone: share one across
tasks.
Shorten links
# async
Anonymous creations return a one-time claim_token. Store it and the link
can be claimed into an account later:
# async
Manage links
# async
Statistics and exports
# async
Account-wide downloads come from stats().export(); per-link downloads with
per-link filenames come from stats().export_link(id).
Public endpoints
# async
Errors
Everything returns Result<T, spoo_me::Error>, and the library never
panics. API failures carry the parsed error envelope plus predicates so you
can branch without string matching:
# async
Transient failures (408, 429, 5xx, connection errors) are retried twice with
jittered exponential backoff capped at 8 seconds, honoring Retry-After in
both its legal forms. Requests that could duplicate work on replay (POST,
PATCH) retry only where the server provably did nothing (429, 503).
Sign in with Spoo
The oauth feature carries the client half of the connected-apps flow:
PKCE, the device-code exchange, and a self-refreshing session.
// Requires the `oauth` feature.
# async
Sessions refresh proactively before the access token expires and once more
after an unexpected 401. Refreshes are single-flight across tasks, and a
dead refresh token surfaces as Error::SessionExpired.
Scope
This SDK covers the v1 data plane: shortening (including emoji aliases), link management, bulk operations, claiming, statistics, exports, public link surfaces, the emoji catalogue, identity read, and Sign in with Spoo. Account administration (API key management, profile editing), service endpoints (health, contact), and the legacy v0 API are deliberately out of scope.
| Area | Methods |
|---|---|
| Shorten | links().create(), links().check_alias() |
| Manage | links().list(), get(), get_by_address(), update(), set_status(), delete(), delete_all_on_domain() |
| Bulk | bulk_delete(), bulk_set_status(), bulk_set_expiry(), bulk_move_domain() |
| Claim | links().claim() |
| Stats | stats().account(), stats().for_link() |
| Export | stats().export(), stats().export_link() |
| Public | public().stats(), public().preview() |
| Emoji | emoji().set() |
| Identity | auth().me() |
| Sign in with Spoo | oauth().authorization_url(), exchange_code(), refresh_tokens(), Session |
Raw requests
For v1 endpoints the SDK does not cover yet, the client exposes typed passthroughs that reuse its auth, retries, timeout and error mapping:
# async
These are a supported pressure valve. If you need one, the surface has a gap worth an issue on this repo.
Features
| Feature | Default | What it adds |
|---|---|---|
stream |
yes | Lazy pagination and export bodies as futures_core::Stream |
oauth |
no | Sign in with Spoo: PKCE, exchange, refreshing sessions |
tracing |
no | Per-request spans and retry events |
native-tls |
no | Platform TLS instead of rustls |
Using oauth on wasm32? getrandom requires the final binary to pick its
backend: add --cfg getrandom_backend="wasm_js" to your RUSTFLAGS (usually
via .cargo/config.toml).
Credits
The original spoo-me crate was created and designed by
rdni. This SDK keeps that crate's design
language: consuming builders, layered errors, and documented-everything
discipline.
License
MIT. Versions up to 0.1.1 were published under Apache-2.0.