6 releases (1 stable)
Uses new Rust 2024
| new 1.0.0 | Mar 5, 2026 |
|---|---|
| 0.3.1 | Aug 5, 2025 |
| 0.2.1 | Aug 5, 2025 |
| 0.1.2 | Aug 5, 2025 |
| 0.1.0 | Apr 11, 2025 |
#1 in #per-page
395KB
3.5K
SLoC
Hetzner API Rust SDK
current version: 1.0.0
A Rust SDK for Hetzner APIs with a domain-oriented structure and typed error handling.
Documentation
- Full docs:
docs/COMPREHENSIVE_DOCUMENTATION.md - API reference:
docs/API_REFERENCE.md - Project-owned OpenAPI:
openapi.json
API Coverage
cloud (openapi-backed)
- Full Hetzner Cloud OpenAPI operation coverage via generated methods on
cloud() - Hand-written typed surfaces for:
-
servers.list -
servers.get -
servers.create(typed response:server,action,next_actions,root_password) -
actions.list(ID-filtered) -
actions.get
dns (legacy)
- GetAllZones
- GetAllRecords
- CreateRecord
- GetRecord
- UpdateRecord
- DeleteRecord
- CreateZone
- GetZone
- UpdateZone
- DeleteZone
Deprecation Notice
The legacy DNS API surface is now deprecated.
- Deprecated style: direct DNS methods on
HetznerClient(for exampleclient.get_all_zones()). - Preferred style: domain APIs (for example
client.dns().list_zones()).
Compatibility wrappers remain available for now and are marked deprecated in code.
Installation
[dependencies]
hetzner = "0.3.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
Quick Start
use hetzner::HetznerClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HetznerClient::new("tbzcPCFQOYdvw5udQKYpaGeeT32GvUix2Iw7T6z0ZVy22c5EnmTVCxgkYOz8b4p5");
// Legacy DNS domain usage (preferred over deprecated direct methods):
let zones = client.dns().list_zones().await?;
println!("{zones:#?}");
Ok(())
}
Cloud Servers Example
use hetzner::{HetznerClient, ListServersParams};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HetznerClient::new("tbzcPCFQOYdvw5udQKYpaGeeT32GvUix2Iw7T6z0ZVy22c5EnmTVCxgkYOz8b4p5");
let params = ListServersParams {
per_page: Some(25),
page: Some(1),
..Default::default()
};
let servers = client.cloud().servers().list(Some(¶ms)).await?;
println!("{servers:#?}");
Ok(())
}
Cloud Actions Example
use hetzner::{HetznerClient, ListActionsParams};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HetznerClient::new("tbzcPCFQOYdvw5udQKYpaGeeT32GvUix2Iw7T6z0ZVy22c5EnmTVCxgkYOz8b4p5");
let actions = client
.cloud()
.actions()
.list(&ListActionsParams { ids: vec![42, 43] })
.await?;
println!("{actions:#?}");
Ok(())
}
Full OpenAPI Operation Example
All operations from hetzner-cloud-openapi.json are generated as methods on CloudApi using their operationId.
use hetzner::HetznerClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HetznerClient::new("tbzcPCFQOYdvw5udQKYpaGeeT32GvUix2Iw7T6z0ZVy22c5EnmTVCxgkYOz8b4p5");
// operationId: list_servers
let servers = client.cloud().list_servers(None, None).await?;
// operationId: get_server
let server = client.cloud().get_server(42_u64, None, None).await?;
println!("{servers:#?}");
println!("{server:#?}");
Ok(())
}
API Facades
The SDK now exposes direct API groups for the areas you requested:
client.dns()legacy DNS API (records/zones)client.cloud().servers_api()full Servers API (CRUD/actions/metrics)client.cloud().domains()Domain/Zone API (zones/rrsets)client.cloud().private_networks()Private Network APIclient.cloud().load_balancers()Load Balancer APIclient.cloud().storage()Storage API (volumes/images/isos)- plus complete raw OpenAPI methods via
client.cloud().<operation_id>(...)
Cloud API Example Request
Since a new project commonly has no servers yet, this is the expected empty list shape.
Example Request
curl -H "Authorization: Bearer tbzcPCFQOYdvw5udQKYpaGeeT32GvUix2Iw7T6z0ZVy22c5EnmTVCxgkYOz8b4p5" \
https://api.hetzner.cloud/v1/servers
Example Response
{
"servers": [],
"meta": {
"pagination": {
"page": 1,
"per_page": 25,
"previous_page": null,
"next_page": null,
"last_page": 1,
"total_entries": 0
}
}
}
Authentication
All requests to the Hetzner Cloud API must be authenticated with an API token.
Use this header:
Authorization: Bearer tbzcPCFQOYdvw5udQKYpaGeeT32GvUix2Iw7T6z0ZVy22c5EnmTVCxgkYOz8b4p5
To create a token: Hetzner Console -> Project -> Security -> API Tokens.
Error Handling
The SDK returns typed errors (HetznerError) and preserves API error payload details.
Hetzner API error payload shape:
code: machine-parsable error identifiermessage: human-readable descriptiondetails: optional object with code-specific fields
Example Error Payload
{
"error": {
"code": "invalid_input",
"message": "invalid input in field 'broken_field': is too long",
"details": {
"fields": [
{
"name": "broken_field",
"messages": ["is too long"]
}
]
}
}
}
Common Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | json_error |
Invalid JSON input in request |
| 401 | unauthorized |
Invalid or unknown token |
| 401 | token_readonly |
Token only allows GET requests |
| 403 | forbidden |
Insufficient permissions |
| 403 | maintenance |
Operation blocked by maintenance |
| 403 | resource_limit_exceeded |
Account/project resource limit exceeded |
| 404 | not_found |
Entity not found |
| 405 | method_not_allowed |
HTTP method not allowed |
| 409 | uniqueness_error |
Uniqueness constraint violation |
| 409 | conflict |
Resource changed during request |
| 410 | deprecated_api_endpoint |
Endpoint functionality removed |
| 412 | resource_unavailable |
Requested resource unavailable |
| 422 | invalid_input |
Input parsing/validation failed |
| 422 | service_error |
Internal service-level error |
| 422 | unsupported_error |
Action unsupported by resource |
| 423 | locked |
Resource locked by running action |
| 423 | protected |
Action protected for resource |
| 429 | rate_limit_exceeded |
Request limit exceeded |
| 500 | server_error |
API backend error |
| 503 | unavailable |
Service/product unavailable |
| 504 | timeout |
Request timeout |
Actions
Actions are asynchronous tasks that may be returned when mutating resources.
- Wait until action state is
successorerror. - Avoid aggressive polling to reduce rate limit pressure.
- Failed actions include additional error context.
Labels
Labels are key/value pairs on resources.
- Keys: optional DNS-like prefix + required name segment (
prefix/name). - Values: up to 63 chars, empty or alphanumeric-bounded with
-,_,.allowed. - Prefix
hetzner.cloud/is reserved.
Example Labels
{
"labels": {
"environment": "development",
"service": "backend",
"example.com/my": "label",
"just-a-key": ""
}
}
Label Selector
Supported selectors include:
k==vork=vk!=vk!kk in (v1,v2,v3)k notin (v1,v2,v3)
Examples:
env=production,type!=database
env in (testing,staging)
!type
Pagination
List endpoints can support page and per_page (default 25, usually max 50).
Responses can include:
meta.paginationin JSONLinkheader withprev,next,last
Example Pagination Payload
{
"servers": [],
"meta": {
"pagination": {
"page": 2,
"per_page": 25,
"previous_page": 1,
"next_page": 3,
"last_page": 4,
"total_entries": 100
}
}
}
Rate Limiting
All requests are rate limited.
Headers:
RateLimit-LimitRateLimit-RemainingRateLimit-Reset(UNIX timestamp)
Default limit is 3600 requests/hour per project, with gradual refill.
Server Metadata
In-server metadata endpoint:
http://169.254.169.254/hetzner/v1/metadata
Available keys include:
hostnameinstance-idpublic-ipv4private-networksavailability-zoneregion
Sorting
Some list endpoints support repeated sort query params.
Examples:
https://api.hetzner.cloud/v1/actions?sort=status
https://api.hetzner.cloud/v1/actions?sort=status:asc
https://api.hetzner.cloud/v1/actions?sort=status:desc
https://api.hetzner.cloud/v1/actions?sort=status:asc&sort=command:desc
Structure
Current crate structure:
client: transport, auth, request lifecycleerror: typed SDK errors and API error envelopestypes: shared response/resource modelsapi::dns: DNS domain operationsapi::cloud: Cloud domain operations (serversfirst)api::cloud::enums: OpenAPI-derived enums for server status/sort and action statusapi::cloud::generated_ops: full OpenAPI-generated operation methods
This layout is intended to support additional Hetzner domains without flattening everything into one module.
Dependencies
~14–32MB
~346K SLoC