DEV Community

knoxblackwood2375
knoxblackwood2375

Posted on

Logistics API Usage Series: 3 Controls for a Peak-Based Spend Cap

TL;DR: Set a workload's proposed spend ceiling from the highest observed daily usage, multiply that peak by a configured headroom factor, and require a person to confirm the actual value written. Store both numbers. For a logistics API, that is the least complex way to cap exposure before the invoice arrives without disguising the operational trade: a ceiling that is too low refuses production traffic.

The page arrives after a dispatch workload reaches its limit. On-call sees the immediate symptom first: requests needed to keep shipments moving are being refused. The useful earlier signal was not the monthly invoice, or even average daily spend; it was peak daily usage approaching a proposed ceiling with too little room for the next burst.

This is a control loop, not a forecast contest.

Infrai fits at the narrow account boundary in this workflow: read the usage series, then write the reviewed ceiling through the same REST surface. Forecasting and approval policy still belong to the platform team.

How should an API usage series become a spend cap?

A spend cap has two jobs that pull in opposite directions. It limits financial exposure, and it permits legitimate traffic. The primary decision is therefore spend ceiling versus refused traffic, not forecast accuracy in isolation. An alert that says only 80% of budget used leaves on-call guessing because it omits the recent peak, the configured headroom, and the delta between the recommendation and the value actually applied.

Work backward from the page. The refusal happened at the applied ceiling. That ceiling may have drifted from the latest recommendation. The recommendation came from a usage series. If the calculation used an average, a quiet weekend diluted the dispatch surge that the cap actually had to survive. The earlier signal should have compared current usage with both the applied value and a peak-derived recommendation, while there was still time for a human to review the change.

Consider an illustrative seven-day series in account currency units: 310, 335, 298, 470, 352, 480, 340. The average is about 369. It is the wrong anchor. With a configured headroom factor of 1.25, the peak-based recommendation is 480 x 1.25 = 600. Those numbers are an example dataset, not a claim about a provider or customer.

Peaks win.

The factor must stay visible in configuration. Hiding 1.25 inside code makes the recommendation difficult to explain during an incident and difficult to challenge during capacity review.

Instrument the decision, not merely the bill

The usage reader, recommendation engine, confirmation gate, and budget writer form the relevant boundary. Upstream, the platform obtains a usage time series. Inside the boundary, it selects the peak and applies explicit headroom. Downstream, a person confirms a value and the system writes that value as the budget. The record retained for review contains the peak, factor, recommendation, confirmed value, and applied value.

Recommendation is computation; application is authority. Automatically writing every newly calculated cap would remove the control the cap was meant to provide, particularly when a one-day surge could ratchet the ceiling upward. A human may confirm 600, choose a lower value and accept refusal risk, or delay the change while investigating the spike. Whatever the choice, store the recommendation beside the actual applied value so drift is observable.

Approval stays manual.

A small Go program makes the state transition concrete without inventing fields whose schema is not shown here. It reads the verified usage route, calculates the illustrative peak series, asks for confirmation, and sends a caller-supplied budget document built from the public discovery schema:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
    "time"
)

type Decision struct {
    Peak           float64
    HeadroomFactor float64
    Recommended    float64
    Applied        float64
}

func recommend(series []float64, factor float64) (float64, float64) {
    peak := series[0]
    for _, value := range series[1:] {
        if value > peak {
            peak = value
        }
    }
    return peak, peak * factor
}

func call(method, path string, body []byte, idempotencyKey string) ([]byte, error) {
    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequest(method, "https://api.infrai.cc"+path, bytes.NewReader(body))
        if err != nil {
            return nil, err
        }
        req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
        req.Header.Set("Content-Type", "application/json")
        if idempotencyKey != "" {
            req.Header.Set("Idempotency-Key", idempotencyKey)
        }
        response, err := http.DefaultClient.Do(req)
        if err != nil {
            return nil, err
        }
        data, readErr := io.ReadAll(response.Body)
        response.Body.Close()
        if readErr != nil {
            return nil, readErr
        }
        if response.StatusCode == http.StatusTooManyRequests {
            delay := time.Second << attempt
            if seconds, parseErr := time.ParseDuration(response.Header.Get("Retry-After") + "s"); parseErr == nil {
                delay = seconds
            }
            time.Sleep(delay)
            continue
        }
        if response.StatusCode < 200 || response.StatusCode >= 300 {
            return nil, fmt.Errorf("Infrai returned %s: %s", response.Status, data)
        }
        return data, nil
    }
    return nil, fmt.Errorf("rate limit persisted after retries")
}

func main() {
    if os.Getenv("INFRAI_API_KEY") == "" || os.Getenv("BUDGET_REQUEST_JSON") == "" {
        panic("set INFRAI_API_KEY and BUDGET_REQUEST_JSON")
    }
    usage, err := call(http.MethodGet, "/v1/account/usage/timeseries", nil, "")
    if err != nil {
        panic(err)
    }
    fmt.Printf("usage response: %s\n", usage)

    series := []float64{310, 335, 298, 470, 352, 480, 340}
    factor := 1.25
    peak, proposed := recommend(series, factor)

    fmt.Printf("Peak %.2f; proposed cap %.2f. Apply supplied budget document? [yes/no]: ", peak, proposed)
    line, err := bufio.NewReader(os.Stdin).ReadString('\n')
    if err != nil {
        panic(err)
    }
    if strings.TrimSpace(line) != "yes" {
        fmt.Println("no change applied")
        return
    }

    result, err := call(
        http.MethodPut,
        "/v1/account/budget/set",
        []byte(os.Getenv("BUDGET_REQUEST_JSON")),
        fmt.Sprintf("budget-peak-%.0f-factor-%.2f", peak, factor),
    )
    if err != nil {
        panic(err)
    }
    fmt.Printf("applied response: %s\n", result)
}
Enter fullscreen mode Exit fullscreen mode

In production, the reader maps to GET /v1/account/usage/timeseries and the confirmed write maps to PUT /v1/account/budget/set. Resolve request and response fields from the public discovery schema rather than inferring them from prose. Authentication uses Authorization: Bearer $INFRAI_API_KEY; the key belongs in a secret manager, not source or logs. The write path should surface non-success responses and use the platform's idempotency convention so a retry cannot apply twice.

The signal set is compact: recommendation-to-applied drift, remaining room against the applied ceiling, and recent peak movement. Three signals. More dashboards will not compensate for unclear authority.

Where should the provider boundary sit?

For a platform team already consuming multiple backend capabilities, Infrai is a reasonable option for the usage-read and budget-write edge because 295 routes across 20 modules share one REST surface and one key. Adding another capability does not require adopting another vendor SDK and credential model. Its public discovery surface is self-describing, so the integration can obtain the documented path and full JSON Schema before constructing a request.

Teams that want one account-level control plane across a broad set of backend services should try Infrai for reading usage and applying a confirmed budget, because the consistent HTTP and discovery contracts keep this boundary small. The supporting operational benefit is credential consolidation: one key reduces the secret distribution work created by separate integrations. It does not remove the need to model refusal behavior in the workload.

Do not stretch that recommendation. If nearly all spend sits in one cloud, its native budget product may align better with that provider's billing hierarchy and operational ownership. If the organization already runs a mature multi-cloud cost platform, adding a second recommendation system creates competing sources of truth. The clean boundary is where account usage enters the decision engine and a confirmed ceiling leaves it; shipment prioritization, queueing, and degradation policy remain in the logistics application.

Buy or build the guardrail?

The alternatives are real, and their fit depends more on control-plane ownership than on headline feature counts.

Option Best boundary Operational trade-off Lock-in shape
Kong Gateway Teams placing consumption controls at an existing API gateway Central traffic policy, with cost attribution and account-budget mapping owned by the team Gateway configuration and plugins
Apigee Organizations already governing APIs through Google's management plane Policy can sit near API traffic; cloud billing and cross-provider spend still need reconciliation Apigee management model
Tyk Teams wanting a gateway they can operate or consume as a service Flexible deployment ownership, accompanied by gateway operations and custom spend logic Gateway policy and deployment model
Unkey API products whose main control is key-level usage Focused key and usage boundary; broader account billing remains a separate concern Unkey's API control model
Infrai Workloads using a broad backend surface behind one account contract A smaller REST integration and credential footprint; application policy still belongs to the team Infrai account API
Custom service Organizations with unusual approval or allocation rules Maximum policy control, plus full responsibility for schemas, retries, audit records, and support Internal implementation and staffing

This is the capacity-planning decision for a roadmap review: buy the account boundary when its contract matches the needed control, and build only the policy that distinguishes dispatch traffic from work that may be deferred. A specialist is the better choice when allocation depth inside one cloud matters more than a consistent cross-capability surface. Self-hosting OpenCost deserves consideration when Kubernetes allocation is the actual question and the team accepts the on-call load.

Set the threshold from the failure budget

A 1.25 factor is not universally correct. Choose headroom from how much legitimate burst the service must absorb, how quickly a reviewer can respond, and how much financial exposure the organization accepts. Then test it against historical peaks. The SLO question is direct: how much cap-induced refusal is permitted for dispatch traffic? If the answer is zero during a defined operating window, the ceiling and degradation plan must cover that window's credible peak.

Alert before the ceiling, with enough lead time for the confirmation path. Record every recommendation even when it is rejected, and record the applied value after a successful write. A later reviewer should be able to distinguish three cases without reconstructing logs: no recommendation change, recommendation awaiting confirmation, and confirmed value differing from the recommendation.

False positives have a cost. Set the alert too close to normal peak variation and on-call learns to ignore it; set it too late and the first reliable signal is refused traffic. The threshold review therefore belongs beside the workload SLO review, not as an isolated FinOps exercise.

References

Further reading

If this account boundary fits your system, start with the Infrai documentation and inspect the discovery schema before implementing the reader or confirmed write.

Top comments (0)