Skip to main content

Client SDKs

The REST API has small Go and Python clients. Both accept a base URL and optional deployment token.

Go​

go get github.com/beremaran/straw-sdk-go@v0.4.0
package main

import (
"context"
"errors"
"fmt"
"log"
"os"
"time"

straw "github.com/beremaran/straw-sdk-go"
)

func main() {
client := straw.NewClient("http://localhost:8080", os.Getenv("STRAW_AUTH_TOKEN"))
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

response, err := client.Do(ctx, straw.Request{
Method: "GET",
URL: "https://example.com",
Routing: &straw.RoutingHints{Country: "AU", Tags: []string{"residential"}, StickySessionID: "checkout-42"},
Headers: []straw.Header{{Name: "X-Request-Source", ValueBase64: "YXBw"}},
})
if err != nil {
var apiErr *straw.APIError
if errors.As(err, &apiErr) {
log.Printf("Straw error code=%s retryable=%t request_id=%s", apiErr.Response.Code, apiErr.Response.Retryable, apiErr.Response.RequestID)
return
}
log.Fatal(err)
}
fmt.Println(response.Status, response.RequestID)
}

The exact v0.4.0 Go tag supports the fields shown above plus Body, FingerprintProfile, TimeoutMs, Replayable, and ResponseBodyMode. Header values are base64-encoded bytes. Non-2xx Straw responses are returned as *straw.APIError with HTTPStatus and the parsed error envelope.

Request.Routing carries optional tags, country, region, IP type, and sticky-session ID constraints. GET, HEAD, and OPTIONS requests become replayable by default; set Replayable: true only when the application operation is safe to retry for other methods.

Create one client per base URL/token and reuse it; the client is safe for concurrent requests through Go's shared HTTP transport. Every call accepts a context, so set a deadline and cancel abandoned work. The SDK does not retry: inspect APIError.Response.Retryable, RetryAfterMs, and application idempotency before replay. Response bodies are fully represented by the bounded API envelope or receipt and require no caller-owned HTTP body close. Proxy failures preserve an optional APIError.Response.UpstreamStatus pointer, including 407, without exposing credentials. Use structured request IDs/error codes in logs and never log tokens, headers, URLs, bodies, or signed receipt references.

For a large body, use CreateReceipt, one or more UploadReceiptPart calls, and CompleteReceipt; then set RequestBody{Mode: "receipt", ReceiptID: receipt.ReceiptID}. Set ResponseBodyMode: "receipt" to store a response, and open it with DownloadReceipt.

This is a one-part request receipt; larger uploads use the same calls with positive part numbers and completion still requires the complete 1..N set:

body := []byte("request body")
sum := sha256.Sum256(body)
receipt, err := client.CreateReceipt(ctx, straw.CreateReceiptInput{
Direction: "request", SizeBytes: int64(len(body)), SHA256Hex: hex.EncodeToString(sum[:]),
})
if err != nil { log.Fatal(err) }
if _, err = client.UploadReceiptPart(ctx, receipt.ReceiptID, 1, bytes.NewReader(body), int64(len(body)), hex.EncodeToString(sum[:])); err != nil {
log.Fatal(err)
}
if _, err = client.CompleteReceipt(ctx, receipt.ReceiptID); err != nil { log.Fatal(err) }
response, err := client.Do(ctx, straw.Request{
Method: "POST", URL: "https://example.com/upload", Replayable: false,
Body: &straw.RequestBody{Mode: "receipt", ReceiptID: receipt.ReceiptID},
})
if err != nil { log.Fatal(err) }
fmt.Println(response.Status, response.RequestID)

Add bytes, crypto/sha256, and encoding/hex to the imports in this receipt example. For a response receipt, set ResponseBodyMode: "receipt", read response.Body.ReceiptID, and close the io.ReadCloser returned by DownloadReceipt after copying it.

Python​

Install the exact public tag:

uv add 'straw-sdk @ git+https://github.com/beremaran/straw-sdk-python.git@v0.2.1'
import os

from straw import APIError, Client, Header, Request, RoutingHints

client = Client("http://localhost:8080", os.getenv("STRAW_AUTH_TOKEN", ""), timeout=30.0)
try:
response = client.do(Request(
method="GET",
url="https://example.com",
routing=RoutingHints(tags=["residential"], country="AU", sticky_session_id="checkout-42"),
headers=[Header(name="X-Request-Source", value_base64="YXBw")],
))
print(response.status, response.request_id)
except APIError as exc:
print(exc.http_status, exc.response.code, exc.response.retryable, exc.response.request_id)

Pass a token as the second Client argument. The exact v0.2.1 package exposes Client, Request, Header, RequestBody, Response, Receipt, and APIError as used above. Non-2xx Straw responses raise straw.APIError; inspect exc.http_status and the typed exc.response envelope. exc.response.upstream_status is None when absent and an integer when Control reports a CONNECT response status.

Reuse a client rather than creating one per request. Supply explicit request timeouts and let cancellation/errors propagate; there is no automatic retry. A shared client may be used by concurrent application tasks according to the tagged package API, while callers remain responsible for their own mutable request data. Log request IDs and stable codes only. Tests can point the client at an httptest/local fake implementing the documented REST contract.

The Python client provides matching create_receipt, upload_receipt_part, complete_receipt, get_receipt, and download_receipt methods. RequestBody(mode="receipt", receipt_id=...) and Request(response_body_mode="receipt", ...) select the receipt paths.

Use RoutingHints(tags=["residential"], country="AU", sticky_session_id="checkout-42") on Request.routing for the same routing contract as REST, proxy, CONNECT, and the Go SDK. GET, HEAD, and OPTIONS default to replayable; set replayable=True for another method only when the operation is safe to retry.

Example request-body upload:

import hashlib

body = b"request body"
digest = hashlib.sha256(body).hexdigest()
receipt = client.create_receipt("request", len(body), digest, "upload-42")
client.upload_receipt_part(receipt.receipt_id, 1, body, digest)
client.complete_receipt(receipt.receipt_id)
response = client.do(Request(
method="POST",
url="https://example.com/upload",
body=RequestBody(mode="receipt", receipt_id=receipt.receipt_id),
))
print(response.status, response.request_id)

The Python package also contains the lower-level worker SDK. See custom workers.

The REST clients are the most stable SDK surface. Worker SDKs follow the negotiated protocol compatibility matrix and may change between pre-1.0 minors. Generated package documentation and source are linked from the public tagged repositories; use only versions listed in Compatibility and versioning.

Both public clients default GET, HEAD, and OPTIONS to replayable; other methods remain non-replayable unless the tagged client request type permits an explicit override. Neither client automatically retries a failed request.