Transient server failures can be retried with a bounded attempt count. Keeping one std.http.Client for all attempts also lets its connection pool reuse eligible keep-alive connections; call request.deinit() after each response so the client can release or reuse it. Only retry failures that are safe for the request and appropriate for the service.

//! Retry transient HTTP failures while reusing one HTTP client.

const std = @import("std");
const http = std.http;

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    const gpa = init.gpa;
    const uri = try std.Uri.parse("https://httpbin.org/status/503");

    var client: http.Client = .{ .allocator = gpa, .io = io };
    defer client.deinit();

    var attempt: u8 = 0;
    while (attempt < 3) : (attempt += 1) {
        var request = try client.request(.GET, uri, .{});
        try request.sendBodiless();

        var response_buffer: [1024]u8 = undefined;
        const response = try request.receiveHead(&response_buffer);
        const status_code = @intFromEnum(response.head.status);
        std.debug.print("attempt {d}: HTTP {d}\n", .{ attempt + 1, status_code });
        request.deinit();

        if (status_code < 500) return;
    }

    std.debug.print("retry limit reached\n", .{});
}