v0.18.2 · stable Go 1.22+ MIT license

A lightweight HTTP client for Go services.

Pebble is a small, opinionated wrapper around net/http with the things you find yourself reimplementing on every team: retries, timeouts, tracing, and structured errors. About 600 lines of code and one dependency.

$ go get github.com/example/pebble@latest

What Pebble gives you

Retry handling

Exponential backoff with jitter, configurable per-request. Only retries on the response codes that make sense to retry on.

Sensible timeouts

Separate dial, TLS handshake, and request timeouts. No more http.Get hanging on a misconfigured load balancer.

Tracing built in

OpenTelemetry spans for every request, with the relevant attributes. Optional, off by default.

Structured errors

Errors that tell you what failed and where. No more wrapping io.EOF in three layers of "unexpected error reading response."

Connection pooling

Per-host connection limits, idle timeouts, and graceful shutdown. The things you remember after one outage.

No magic

Configuration is explicit. Defaults are documented. There are no global state changes. Read the code, it's short.

A minimal example

The smallest useful program with Pebble:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/example/pebble"
)

func main() {
    client := pebble.New(pebble.Options{
        Timeout:    pebble.DefaultTimeout,
        MaxRetries: 3,
    })

    resp, err := client.Get(context.Background(), "https://api.example.com/v1/health")
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    fmt.Println("Status:", resp.StatusCode)
}

That's it. Sensible behaviour out of the box, with knobs available when you need them.

Read the documentation →