#dns #dns-records #cloud-api #per-page #rust-sdk #pagination #cloud-server

bin+lib hetzner

A Rust library for managing DNS records and zones on Hetzner's platform, providing tools for networking and DNS management

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

MIT license

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 example client.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(&params)).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 API
  • client.cloud().load_balancers() Load Balancer API
  • client.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 identifier
  • message: human-readable description
  • details: 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 success or error.
  • 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==v or k=v
  • k!=v
  • k
  • !k
  • k 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.pagination in JSON
  • Link header with prev, 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-Limit
  • RateLimit-Remaining
  • RateLimit-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:

  • hostname
  • instance-id
  • public-ipv4
  • private-networks
  • availability-zone
  • region

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 lifecycle
  • error: typed SDK errors and API error envelopes
  • types: shared response/resource models
  • api::dns: DNS domain operations
  • api::cloud: Cloud domain operations (servers first)
  • api::cloud::enums: OpenAPI-derived enums for server status/sort and action status
  • api::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