*** title: Go SDK description: Integrate feature flags in your Go services using the Unleash Go SDK 'og:site\_name': Unleash 'og:title': Go SDK keywords: 'Go SDK, feature flags, backend, golang, unleash' max-toc-depth: 3 ---------------- The [Unleash Go SDK](https://github.com/Unleash/unleash-go-sdk) lets you evaluate feature flags in your Go services and applications. You can use this SDK with [Unleash Enterprise](https://www.getunleash.io/pricing) or [Unleash Open Source](https://github.com/Unleash/unleash). ## Requirements * Go 1.21 or later (the SDK is tested against Go 1.21.x through 1.26.x) ## Installation Install the SDK module: ```bash go get github.com/Unleash/unleash-go-sdk/v6@latest ``` ## Configuration Pass configuration options to `unleash.Initialize(...)` (global client) or `unleash.NewClient(...)` (instance client). Base URL to your Unleash or Edge API, for example: `https:///api/`. Name of your application. Included in registration, metrics, and request headers. Additional headers for all API requests. Use this to add Authorization. Environment value included in the static Unleash context. Explicit instance ID for this SDK process. If omitted, the SDK generates one. How often (in seconds) the SDK polls for updated flag configuration. How often (in seconds) the SDK sends usage metrics to Unleash. Disables client registration and metrics posting when set to `true`. Restricts flag fetches to a single project. Prefer using a [project scoped API token](/concepts/api-tokens-and-client-keys#api-token-format) instead. Directory used by storage implementations for persisted SDK state. Custom storage implementation for local persistence and bootstrapping. See [bootstrap flag data](#bootstrap-flag-data). Registers custom activation strategies. See [custom strategies](#custom-strategies). Custom `*http.Client` for transport settings. Configure proxy, TLS, and timeouts using the [Go standard library](https://pkg.go.dev/net/http#Client)`http.Client`. This is also how you configure an outbound network proxy. Listener implementation for SDK [events](#events) (errors, ready/update, metrics, impression data). ## Initialization Initialize the SDK early in your application's lifecycle. ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } defer unleash.Close() } ``` ### Connection options Backend SDKs communicate with Unleash or [Unleash Edge](/unleash-edge) through the [Client API](/api#client-api) and require a [backend token](/concepts/api-tokens-and-client-keys#backend-tokens). They fetch flag configuration and evaluate flags locally in your application. Set `WithUrl` to the base API endpoint of your Unleash or [Unleash Edge](/unleash-edge) instance, typically `https:///api/`. The URL pattern is the same in both cases. Use [Unleash Edge](/unleash-edge) when you need lower latency or higher availability. Set `WithCustomHeaders` to include an `Authorization` header with a [backend token](/concepts/api-tokens-and-client-keys#backend-tokens). See [API tokens](/concepts/api-tokens-and-client-keys) for how to generate one. ### Configure an HTTP proxy The SDK uses Go's standard HTTP proxy environment variables. Set `HTTPS_PROXY` or `HTTP_PROXY` in the environment where your service runs: ```bash export HTTPS_PROXY=http://proxy.internal:8080 export NO_PROXY=localhost,127.0.0.1,.internal ``` ### Wait until the SDK is ready Until the SDK loads flag data, boolean checks return false and variant checks return a disabled variant. See [Check flags](#check-flags) for details on evaluation methods and fallback behavior. 1. **Bootstrap data**: if a bootstrap is provided the SDK loads data from it synchronously on creation, if the bootstrap fails to load it will fall back to cached data. 2. **Cached data**: if no bootstrap is available, the SDK attempts to load data from persistent storage synchronously on creation. 3. **Network fetch**: the SDK fetches flag configuration from Unleash asynchronously on startup. The ready event fires on the first successful response from Unleash. If bootstrap or cached data is available, the SDK can evaluate flags immediately. There is no need to wait for the network fetch. Use `WaitForReady` to block until the SDK has completed its first network fetch. This is useful when you need guaranteed fresh data, but it will block even if bootstrap or cached data is already loaded. If the API is unreachable, `WaitForReady` blocks indefinitely. ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } unleash.WaitForReady() } ``` ## Check flags ### Check if a flag is enabled Use `IsEnabled` with `FeatureOptions`: ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } enabled := unleash.IsEnabled("my-feature", unleash.FeatureOptions{}) if enabled { // new behavior } } ``` `FeatureOptions` controls how `IsEnabled` evaluates a flag: Evaluation context for this call. See [Unleash context](#unleash-context) for how to pass the same context to both `IsEnabled` and `GetVariant`. Fallback value used when the flag cannot be resolved and `FallbackFunc` is not set. Fallback callback used when the flag cannot be resolved. If set, this takes precedence over `Fallback`. Override for resolving flags without repository lookup. This exists for backwards compatibility and is usually not needed in v6. Fallback precedence when a flag is missing: 1. `FallbackFunc` 2. `Fallback` 3. `false` Example with fallback: ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" unleashcontext "github.com/Unleash/unleash-go-sdk/v6/context" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } enabled := unleash.IsEnabled("my-feature", unleash.FeatureOptions{ FallbackFunc: func(feature string, _ *unleashcontext.Context) bool { return true }, }) if enabled { // New behaviour } } ``` ### Check a flag's variant Use `GetVariant` with `VariantOptions`: ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } defer unleash.Close() variant := unleash.GetVariant("checkout-experiment", unleash.VariantOptions{}) if variant.Name == "blue" { // blue variant behavior } else if variant.Name == "green" { // green variant behavior } } ``` `VariantOptions` controls how `GetVariant` evaluates variants: Evaluation context for this call. See [Unleash context](#unleash-context) for how to pass the same context to both `IsEnabled` and `GetVariant`. Fallback variant used when no variant can be resolved and `VariantFallbackFunc` is not set. Fallback callback used when no variant can be resolved. If set, this takes precedence over `VariantFallback`. Override for resolving flags without repository lookup. This exists for backwards compatibility and is usually not needed in v6. Fallback precedence when a variant cannot be resolved: 1. `VariantFallbackFunc` 2. `VariantFallback` 3. SDK default disabled variant Example with variant fallback: ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" "github.com/Unleash/unleash-go-sdk/v6/api" unleashcontext "github.com/Unleash/unleash-go-sdk/v6/context" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } defer unleash.Close() fallbackVariant := &api.Variant{ Name: "fallback", Enabled: true, FeatureEnabled: true, Payload: api.Payload{ Type: "string", Value: "default", }, } variant := unleash.GetVariant("checkout-experiment", unleash.VariantOptions{ VariantFallbackFunc: func(feature string, _ *unleashcontext.Context) *api.Variant { if feature == "checkout-experiment" { return fallbackVariant } return api.GetDefaultVariant() }, }) if variant.Enabled { // use variant.Name and variant.Payload } } ``` The SDK can return two forms of the disabled variant: * Feature disabled or not found: `{ name: "disabled", enabled: false, featureEnabled: false }` * Feature enabled but no variant resolved (for example no variants configured): `{ name: "disabled", enabled: false, featureEnabled: true }` In both cases, `name` is `disabled`. The `enabled: false` field marks this as the SDK's default fallback variant, which disambiguates it from a real variant that happens to be named `"disabled"`. ## Unleash context Use [Unleash context](/concepts/unleash-context) to drive [strategy](/concepts/activation-strategies) and [constraint](/concepts/activation-strategies#constraints) evaluation for both `IsEnabled` and `GetVariant`. ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" unleashcontext "github.com/Unleash/unleash-go-sdk/v6/context" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } defer unleash.Close() ctx := unleashcontext.Context{ UserId: "user-123", SessionId: "session-abc", Properties: map[string]string{ "plan": "enterprise", }, } enabled := unleash.IsEnabled("my-feature", unleash.FeatureOptions{Ctx: ctx}) variant := unleash.GetVariant("checkout-experiment", unleash.VariantOptions{Ctx: ctx}) _ = enabled enabled := unleash.IsEnabled("my-feature", unleash.FeatureOptions{Ctx: ctx}) if enabled { // New behaviour } variant := unleash.GetVariant("checkout-experiment", unleash.VariantOptions{Ctx: ctx}) if variant.flagEnabled { // checking variant name to pick New behaviour } } ``` For stable gradual rollout and stickiness, include at least `userId` or `sessionId`. ## Bootstrap flag data Bootstrapping lets the SDK start from predefined flag configuration instead of waiting for the first network response. ```go file, err := os.Open("bootstrap.json") if err != nil { panic(err) } defer file.Close() err = unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), unleash.WithStorage(&unleash.BootstrapStorage{Reader: file}), ) if err != nil { panic(err) } ``` `BootstrapStorage` falls back to the default on-disk cache if bootstrap parsing fails. ### Custom bootstrap If you need more control over the bootstrap source, you can implement your own `Storage` and pass it with `WithStorage(...)`. ```go package main import ( "encoding/json" "net/http" "os" unleash "github.com/Unleash/unleash-go-sdk/v6" "github.com/Unleash/unleash-go-sdk/v6/api" ) type CustomBootstrapStorage struct { Backing unleash.DefaultStorage BootstrapJSON []byte } func (s *CustomBootstrapStorage) Init(backupPath, appName string) { s.Backing.Init(backupPath, appName) } func (s *CustomBootstrapStorage) Load() (*api.FeatureResponse, error) { if len(s.BootstrapJSON) > 0 { var state api.FeatureResponse if err := json.Unmarshal(s.BootstrapJSON, &state); err == nil { return &state, nil } } // Fallback to persisted cache when bootstrap data is missing or invalid. return s.Backing.Load() } func (s *CustomBootstrapStorage) Persist(state *api.FeatureResponse) error { return s.Backing.Persist(state) } func main() { bootstrapJSON, err := os.ReadFile("bootstrap.json") if err != nil { panic(err) } err = unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), unleash.WithStorage(&CustomBootstrapStorage{BootstrapJSON: bootstrapJSON}), ) if err != nil { panic(err) } defer unleash.Close() } ``` Your bootstrap payload must match the `/client/features` [response format](/api/get-all-client-features#response) ## Local caching and offline behavior The default storage persists the latest feature payload to `{backupPath}/unleash-repo-schema-v1-{appName}.json` By default, `backupPath` is `os.TempDir()`. On startup, the SDK loads cached state first, then fetches fresh data from Unleash. If Unleash is temporarily unreachable: * The SDK continues serving cached or bootstrapped values. * Polling continues on `WithRefreshInterval`. * Flag evaluations fall back to `false` (or configured fallbacks) when no data exists. ## Events Implement listener interfaces and pass them with `WithListener(...)`. ```go type MyListener struct{} func (l *MyListener) OnError(err error) {} func (l *MyListener) OnWarning(warning error) {} func (l *MyListener) OnReady() {} func (l *MyListener) OnUpdate() {} func (l *MyListener) OnCount(name string, enabled bool) {} func (l *MyListener) OnSent(payload unleash.MetricsData) {} func (l *MyListener) OnRegistered(payload unleash.ClientData) {} func (l *MyListener) OnImpression(event unleash.ImpressionEvent) {} ``` | Event callback | Description | | ------------------------------- | ------------------------------------------------------------------------------- | | `OnReady()` | First successful flag sync completed. | | `OnUpdate()` | A later flag update was fetched. | | `OnError(error)` | SDK encountered an error. | | `OnWarning(error)` | SDK emitted a warning (for example, deprecated URL format). | | `OnCount(string, bool)` | A flag evaluation was counted for metrics. | | `OnSent(MetricsData)` | Metrics payload was sent to Unleash. | | `OnRegistered(ClientData)` | SDK client registration was sent to Unleash. | | `OnImpression(ImpressionEvent)` | A flag was evaluated with [impression data](/concepts/impression-data) enabled. | Use `unleash.DebugListener{}` during development for built-in logging of all event types. ## Impact metrics [Impact metrics](/concepts/impact-metrics) are lightweight, application-level time-series metrics stored and visualized directly inside Unleash. Use them to connect application data, such as request counts, error rates, or latency, to your feature flags and release plans. The SDK automatically attaches `appName`, and `environment` labels to all impact metrics. Initialize the SDK and publish impact metrics with package-level functions. ```go package main import ( "net/http" unleash "github.com/Unleash/unleash-go-sdk/v6" ) func main() { err := unleash.Initialize( unleash.WithAppName("my-go-service"), unleash.WithEnvironment("production"), unleash.WithUrl("https:///api/"), unleash.WithCustomHeaders(http.Header{"Authorization": {""}}), ) if err != nil { panic(err) } defer unleash.Close() unleash.DefineCounter("request_count", "Total number of HTTP requests") unleash.IncrementCounter("request_count") unleash.DefineGauge("active_users", "Current number of active users") unleash.UpdateGauge("active_users", 42) unleash.DefineHistogram( "request_time_ms", "Request processing time in milliseconds", 50, 100, 200, 500, 1000, ) unleash.ObserveHistogram("request_time_ms", 125) } ``` ### Metric types Use counters for cumulative values that only increase, such as the total number of requests or errors. ```go unleash.DefineCounter("request_count", "Total number of HTTP requests") unleash.IncrementCounter("request_count") ``` Use gauges for values that can go up or down, such as current memory usage or active user count. ```go unleash.DefineGauge("active_users", "Current number of active users") unleash.UpdateGauge("active_users", 42) ``` Histograms measure distributions such as latency and payload size. ```go unleash.DefineHistogram( "request_time_ms", "Request processing time in milliseconds", 50, 100, 200, 500, 1000, ) unleash.ObserveHistogram("request_time_ms", 125) ``` Impact metrics are batched and sent on the same interval as standard SDK metrics (`WithMetricsInterval`). ## Custom strategies Register custom strategies with `WithStrategies(...)`. ```go package main import ( unleashcontext "github.com/Unleash/unleash-go-sdk/v6/context" ) type EmailStrategy struct{} func (s EmailStrategy) Name() string { return "ActiveForUserWithEmail" } func (s EmailStrategy) IsEnabled(params map[string]any, ctx *unleashcontext.Context) bool { if ctx == nil || ctx.Properties == nil { return false } return ctx.Properties["email"] == "user@example.com" } ``` Then pass it: ```go unleash.WithStrategies(&EmailStrategy{}) ``` ## Unit testing For unit tests, use an in-memory bootstrap storage and disable metrics. This gives deterministic flag state without depending on a real Unleash instance. The Go SDK does not have a direct `disableRefresh` option, so tests typically set a long `WithRefreshInterval(...)`. The SDK exposes the `NewClient` method so you can create scoped instances for testing purposes, rather than the singleton instance created with `Initialize`. ```go package mypkg_test import ( "testing" "time" unleash "github.com/Unleash/unleash-go-sdk/v6" "github.com/Unleash/unleash-go-sdk/v6/api" ) type testStorage map[string]api.Feature func (s testStorage) Init(string, string) {} func (s testStorage) Load() (*api.FeatureResponse, error) { features := make([]api.Feature, 0, len(s)) for _, f := range s { features = append(features, f) } return &api.FeatureResponse{ Features: features, Segments: []api.Segment{}, }, nil } func (s testStorage) Persist(*api.FeatureResponse) error { return nil } func TestFlags(t *testing.T) { client, err := unleash.NewClient( unleash.WithAppName("test-app"), unleash.WithUrl("http://127.0.0.1:4242/api/"), unleash.WithDisableMetrics(true), unleash.WithRefreshInterval(24*time.Hour), unleash.WithStorage(testStorage{ "my-feature": { Name: "my-feature", Enabled: true, Strategies: []api.Strategy{ {Name: "default"}, }, Variants: []api.VariantInternal{ { Variant: api.Variant{ Name: "blue", Enabled: true, FeatureEnabled: true, }, Weight: 1000, }, }, }, }), ) if err != nil { t.Fatal(err) } defer client.Close() if !client.IsEnabled("my-feature", unleash.FeatureOptions{}) { t.Fatal("expected my-feature to be enabled") } variant := client.GetVariant("my-feature", unleash.VariantOptions{}) if variant.Name != "blue" { t.Fatalf("expected variant blue, got %s", variant.Name) } } ``` ## Troubleshooting If `WaitForReady()` blocks indefinitely: * Verify `WithUrl` points to your Unleash API base URL (for example, `https:///api/`). * Verify `WithCustomHeaders` includes a valid backend API token in `Authorization`. * Confirm network connectivity from your service to the Unleash instance. * Attach `unleash.DebugListener{}` or your own `OnError`/`OnWarning` handlers to inspect failures. If `IsEnabled` always returns `false`: * Confirm the flag exists (check for typos). * Confirm the flag is enabled in the target environment. * Verify your [Unleash context](/concepts/unleash-context) fields match your rollout strategy and constraints. * Verify the SDK completed initial sync (`OnReady` fired, or `WaitForReady()` returned).