go-unifi

Traffic Flows

Query the UniFi controller's traffic-flow log with GetTrafficFlows — a filtered, paginated view of allowed and blocked connections.

A traffic flow is one connection the controller observed — a source, a destination, the protocol and service, which firewall policy acted on it, how many bytes moved, and when. go-unifi exposes the controller's flow log through a single Internal API method, GetTrafficFlows, which takes a filter/pagination request and returns one page of results.

This is the data behind the controller's "Traffic" / flow-insights views, useful for auditing what a firewall rule actually matched.

Fetch a page of flows

You build a *unifi.TrafficFlowsRequest, set whatever filters you want, and read the page out of the *unifi.TrafficFlowsResponse.

import (
	"context"
	"fmt"
	"time"

	"github.com/filipowm/go-unifi/v2/unifi"
)

func recentBlocks(ctx context.Context, c unifi.Client) error {
	req := &unifi.TrafficFlowsRequest{
		Action:        []string{"BLOCK"},
		TimestampFrom: time.Now().Add(-time.Hour).UnixMilli(),
		TimestampTo:   time.Now().UnixMilli(),
		PageNumber:    0,
		PageSize:      50,
	}

	resp, err := c.GetTrafficFlows(ctx, "default", req)
	if err != nil {
		return fmt.Errorf("fetching traffic flows: %w", err)
	}

	for _, flow := range resp.Data {
		fmt.Printf("%s %s -> %s  %s/%s  rx=%d bytes\n",
			flow.Action,
			flow.Source.IP,
			flow.Destination.IP,
			flow.Protocol,
			flow.Service,
			flow.TrafficData.BytesRx,
		)
	}
	fmt.Printf("page %d of %d (total %d flows)\n",
		resp.PageNumber, resp.TotalPageCount, resp.TotalElementCount)
	return nil
}

Each TrafficFlow carries a Source and Destination (TrafficFlowTarget — IP, MAC, hostname, client name, network and zone), the matched Policies (TrafficFlowPolicy), and byte/packet counters in TrafficData.

Filtering

TrafficFlowsRequest has a wide set of optional filter fields — most are []string slices that the controller treats as "match any of these". Leaving a field at its zero value means "don't filter on it". A few of the common ones:

FieldMeaning
Actione.g. "ALLOW", "BLOCK"
Riskrisk level reported for the flow
Protocol"tcp", "udp", …
SourceIP / DestinationIPmatch specific endpoints
SourceMAC / DestinationMACmatch specific clients
SourceNetworkID / DestinationNetworkIDmatch a network
SearchTextfree-text search
TimestampFrom / TimestampTotime window, Unix milliseconds

See the TrafficFlowsRequest reference for the complete list.

TimestampFrom and TimestampTo are Unix milliseconds (int64), not seconds — use time.Time.UnixMilli(), not Unix(). Passing seconds asks for flows from 1970.

Paging through results

GetTrafficFlows returns one page. The response tells you whether more pages exist via HasNext; advance by incrementing PageNumber.

func allFlows(ctx context.Context, c unifi.Client) error {
	req := &unifi.TrafficFlowsRequest{PageNumber: 0, PageSize: 100}
	for {
		resp, err := c.GetTrafficFlows(ctx, "default", req)
		if err != nil {
			return fmt.Errorf("fetching traffic flows page %d: %w", req.PageNumber, err)
		}
		for _, flow := range resp.Data {
			fmt.Printf("%s: %s\n", flow.ID, flow.Risk)
		}
		if !resp.HasNext {
			break
		}
		req.PageNumber++
	}
	return nil
}

Traffic flows are an Internal API feature and are unrelated to the Official API list endpoints and their ListOptions/Page[T] paging model. The flow log uses its own page-number request shape shown above.

Next steps

On this page