Linkbreakers Go SDK: Complete Integration Guide

Learn how to integrate the Linkbreakers Go SDK in your backend application for programmatic link management, visitor tracking, QR code generation, and analytics. Includes installation, authentication, and real code examples for various Go frameworks.

Developer
3 min read
By Laurent Schaffner
Updated March 31, 2025

Short answer

The Linkbreakers Go SDK enables programmatic link management, visitor identification, QR code generation, and analytics access from your Go backend applications. Install it with go get github.com/linkbreakers-com/linkbreakers-go, authenticate with a secret API key, and start creating trackable links for your campaigns, products, or events.

Installation

Install the SDK using Go modules:

Bash
go get github.com/linkbreakers-com/linkbreakers-go

The SDK will be automatically added to your go.mod file:

Go
require github.com/linkbreakers-com/linkbreakers-go v1.0.0

Understanding API keys for Go

The Go SDK is designed for backend/server-side use only and requires a secret API key with full workspace access.

Secret keys

Backend-only. These JWT tokens grant full API access and must never be exposed in public repositories or client-side code.

What they allow:

  • Create, update, and delete links
  • Generate and customize QR codes
  • Identify and track visitors
  • Access analytics and metrics
  • Manage workspace resources
  • Full API access

Where to use:

  • Web servers (Gin, Echo, Fiber)
  • gRPC services
  • Background workers and cron jobs
  • Microservices
  • CLI tools
  • API gateways

Security note: Go applications typically run on servers, making them perfect for secret key usage. Never log keys, commit them to git, or expose them in error messages.

Creating a secret API key

Create your API key from the dashboard:

  1. Log in to app.linkbreakers.com
  2. Navigate to Dashboard β†’ API Tokens
  3. Click Create API Token
  4. Select Secret Key (full API access)
  5. Give it a descriptive name (e.g., "Production Backend" or "Go Service")
  6. Copy the generated JWT token immediately

Important: You won't see the key again after creation. Store it in environment variables or a secret management system.

Quick start

Initialize the client and create your first shortened link:

Go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func main() {
    // Configure API client with your secret key
    config := linkbreakers.NewConfiguration()
    config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("LINKBREAKERS_SECRET_KEY")))
    config.Servers = linkbreakers.ServerConfigurations{
        {URL: "https://api.linkbreakers.com"},
    }

    client := linkbreakers.NewAPIClient(config)
    ctx := context.Background()

    // Create a shortened link
    createReq := linkbreakers.NewCreateLinkRequest("https://example.com/product")
    createReq.SetName("Product Launch - Spring 2025")
    createReq.SetTags([]string{"marketing", "product-launch"})

    response, _, err := client.LinksAPI.LinksServiceCreate(ctx).CreateLinkRequest(*createReq).Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Short URL: %s\n", response.Link.GetShortlink())
    fmt.Printf("Link ID: %s\n", response.Link.GetId())
    fmt.Printf("QR Code: %s\n", response.Link.GetQrcodeSignedUrl())
}

Environment variables (best practice)

Never hardcode API keys. Use environment variables:

Go
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func getClient() *linkbreakers.APIClient {
    config := linkbreakers.NewConfiguration()
    config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("LINKBREAKERS_SECRET_KEY")))
    config.Servers = linkbreakers.ServerConfigurations{
        {URL: "https://api.linkbreakers.com"},
    }
    return linkbreakers.NewAPIClient(config)
}

func main() {
    client := getClient()
    ctx := context.Background()

    // Use the client for API operations
    // ...
}

Set your environment variable:

Bash
# .env file (use godotenv to load)
export LINKBREAKERS_SECRET_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ3b3Jrc3BhY2VfaWQiOiIxMjM0IiwidHlwZSI6InNlY3JldCJ9...

Loading .env files with godotenv:

Go
package main

import (
    "log"

    "github.com/joho/godotenv"
)

func init() {
    // Load .env file
    if err := godotenv.Load(); err != nil {
        log.Println("No .env file found")
    }
}
Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func createBasicLink(client *linkbreakers.APIClient) {
    ctx := context.Background()

    createReq := linkbreakers.NewCreateLinkRequest("https://example.com/summer-sale")
    createReq.SetName("Summer Sale 2025")
    createReq.SetTags([]string{"campaign", "summer", "email"})

    response, _, err := client.LinksAPI.LinksServiceCreate(ctx).CreateLinkRequest(*createReq).Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created: %s\n", response.Link.GetShortlink())
}
Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func createAdvancedLinks(client *linkbreakers.APIClient) {
    ctx := context.Background()

    // Create link with custom shortlink
    customReq := linkbreakers.NewCreateLinkRequest("https://example.com/docs")
    customReq.SetShortlink("docs") // Creates lbr.ai/docs
    customReq.SetName("Documentation Portal")
    customReq.SetFallbackDestination("https://example.com/404")
    customReq.SetTags([]string{"internal", "docs"})

    customLink, _, err := client.LinksAPI.LinksServiceCreate(ctx).CreateLinkRequest(*customReq).Execute()
    if err != nil {
        log.Fatal(err)
    }

    // Create link with metadata for tracking
    trackedReq := linkbreakers.NewCreateLinkRequest("https://example.com/product/123")
    trackedReq.SetName("Product 123 - Email Campaign")
    trackedReq.SetMetadata(map[string]string{
        "product_id":   "123",
        "campaign_id":  "spring-2025",
        "utm_source":   "email",
        "utm_medium":   "newsletter",
        "utm_campaign": "product-launch",
    })
    trackedReq.SetTags([]string{"product", "email"})

    trackedLink, _, err := client.LinksAPI.LinksServiceCreate(ctx).CreateLinkRequest(*trackedReq).Execute()
    if err != nil {
        log.Fatal(err)
    }

    // Create link with QR code (wait for generation)
    qrReq := linkbreakers.NewCreateLinkRequest("https://example.com/event-registration")
    qrReq.SetName("Conference 2025 Registration")
    qrReq.SetWaitForQrcode(true) // Blocks until QR is generated
    qrReq.SetTags([]string{"event", "conference", "qr-code"})

    qrLink, _, err := client.LinksAPI.LinksServiceCreate(ctx).CreateLinkRequest(*qrReq).Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Download QR code: %s\n", qrLink.Link.GetQrcodeSignedUrl())
}
Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func listLinks(client *linkbreakers.APIClient) {
    ctx := context.Background()

    // List links with filtering
    pageSize := int32(50)
    sortBy := linkbreakers.LINKBREAKERSV1LINKSSORTFIELD_UPDATED_AT
    sortDirection := linkbreakers.LINKBREAKERSV1SORTDIRECTION_DESC

    response, _, err := client.LinksAPI.LinksServiceList(ctx).
        PageSize(pageSize).
        Tags([]string{"marketing", "email"}).
        Search("campaign").
        SortBy(sortBy).
        SortDirection(sortDirection).
        Execute()

    if err != nil {
        log.Fatal(err)
    }

    for _, link := range response.GetLinks() {
        fmt.Printf("%s: %s\n", link.GetName(), link.GetShortlink())
        fmt.Printf("  Tags: %v\n", link.GetTags())
        fmt.Printf("  Created: %s\n", link.GetCreatedAt())
    }
}
Go
package main

import (
    "context"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func updateAndDeleteLink(client *linkbreakers.APIClient, linkID string) {
    ctx := context.Background()

    // Get a specific link
    linkResp, _, err := client.LinksAPI.LinksServiceGet(ctx, linkID).
        Include([]string{"tags", "qrcodeSignedUrl"}).
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    // Update link
    updateReq := linkbreakers.NewUpdateLinkRequest()
    updateReq.SetName("Updated Campaign Name")
    updateReq.SetTags([]string{"marketing", "q2-2025", "updated"})

    _, _, err = client.LinksAPI.LinksServiceUpdate(ctx, linkResp.Link.GetId()).
        UpdateLinkRequest(*updateReq).
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    // Delete link
    _, err = client.LinksAPI.LinksServiceDelete(ctx, linkResp.Link.GetId()).Execute()
    if err != nil {
        log.Fatal(err)
    }
}

Create hundreds of links efficiently in a single API call:

Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

type Product struct {
    ID   string
    Name string
    URL  string
}

func createBulkLinks(client *linkbreakers.APIClient) {
    ctx := context.Background()

    // Prepare bulk links (e.g., from a product catalog)
    products := []Product{
        {ID: "001", Name: "Widget A", URL: "https://example.com/products/001"},
        {ID: "002", Name: "Widget B", URL: "https://example.com/products/002"},
        {ID: "003", Name: "Widget C", URL: "https://example.com/products/003"},
    }

    var linksToCreate []linkbreakers.CreateLinkRequest
    for _, p := range products {
        req := linkbreakers.NewCreateLinkRequest(p.URL)
        req.SetName(fmt.Sprintf("Product: %s", p.Name))
        req.SetTags([]string{"product", "catalog"})
        req.SetMetadata(map[string]string{
            "product_id": p.ID,
            "sku":        fmt.Sprintf("SKU-%s", p.ID),
        })
        linksToCreate = append(linksToCreate, *req)
    }

    // Create all links in one request
    bulkReq := linkbreakers.NewCreateBulkLinksRequest(linksToCreate)
    response, _, err := client.LinksAPI.LinksServiceCreateBulk(ctx).
        CreateBulkLinksRequest(*bulkReq).
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created %d links\n", len(response.GetLinks()))
    for _, link := range response.GetLinks() {
        fmt.Printf("  %s: %s\n", link.GetName(), link.GetShortlink())
    }
}

When to use bulk creation:

  • Importing product catalogs
  • Generating links from database records
  • Campaign setup with many URLs
  • Automated link generation pipelines

Visitor identification and tracking

Track and identify visitors using their LBID (Linkbreakers ID) from click/scan events:

Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func identifyVisitor(client *linkbreakers.APIClient, lbid string) {
    ctx := context.Background()

    // Prepare visitor data
    visitorData := map[string]interface{}{
        // System fields (prefixed with "$")
        "$email":     "john.doe@example.com",
        "$phone":     "+14155551234",
        "$firstName": "John",
        "$lastName":  "Doe",

        // Custom attributes (no "$" prefix)
        "company":    "Acme Corporation",
        "jobTitle":   "Product Manager",
        "plan":       "enterprise",
        "signupDate": "2024-01-15",
        "industry":   "SaaS",
        "leadScore":  85,
    }

    visitorInput := linkbreakers.NewVisitorInput()
    visitorInput.SetData(visitorData)

    identifyReq := linkbreakers.NewIdentifyRequest(lbid, *visitorInput)
    identifyReq.SetSetOnce(false) // True = only update empty fields

    response, _, err := client.VisitorsAPI.VisitorsServiceIdentify(ctx).
        IdentifyRequest(*identifyReq).
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("New profile created: %v\n", response.GetCreated())
    fmt.Printf("Visitor ID: %s\n", response.Visitor.GetId())
    fmt.Printf("Email: %s\n", response.Visitor.GetEmail())
}

Understanding LBID:

When visitors click your Linkbreakers links with conversion tracking enabled, a ?lbid=... parameter is added to the destination URL. This LBID connects the visitor to the specific link click, enabling attribution and analytics.

Extract LBID from incoming requests and pass it to the identification API to build visitor profiles. Learn more in our What is LBID? guide.

Working with visitor data

Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func workWithVisitors(client *linkbreakers.APIClient, visitorID string) {
    ctx := context.Background()

    // Get visitor details
    visitor, _, err := client.VisitorsAPI.VisitorsServiceGet(ctx, visitorID).
        Include([]string{"devices", "events", "links"}).
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Visitor: %s\n", visitor.GetEmail())
    fmt.Printf("Devices: %d\n", len(visitor.GetDevices()))
    fmt.Printf("Events: %d\n", len(visitor.GetEvents()))

    // List visitors with filtering
    pageSize := int32(100)
    visitorsList, _, err := client.VisitorsAPI.VisitorsServiceList(ctx).
        PageSize(pageSize).
        Email("user@example.com").
        LinkId("link-uuid").
        Search("Acme Corp").
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    // Update visitor attributes
    updateData := map[string]interface{}{
        "$email":       "updated@example.com",
        "plan":         "enterprise",
        "lastActivity": "2025-03-31",
    }

    visitorUpdate := linkbreakers.NewVisitorInput()
    visitorUpdate.SetData(updateData)

    updateReq := linkbreakers.NewVisitorsServiceUpdateBody(*visitorUpdate)

    _, _, err = client.VisitorsAPI.VisitorsServiceUpdate(ctx, visitorID).
        VisitorsServiceUpdateBody(*updateReq).
        Execute()
    if err != nil {
        log.Fatal(err)
    }
}

Accessing analytics and metrics

Retrieve event data and workspace metrics:

Go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func getAnalytics(client *linkbreakers.APIClient, linkID string) {
    ctx := context.Background()

    // Get events for a specific link (last 30 days)
    endDate := time.Now()
    startDate := endDate.AddDate(0, 0, -30)

    eventsResp, _, err := client.EventsAPI.EventsServiceList(ctx).
        LinkId(linkID).
        StartDate(startDate).
        EndDate(endDate).
        PageSize(1000).
        Include([]string{"visitor", "device", "link"}).
        Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Total events: %d\n", len(eventsResp.GetEvents()))
    for i, event := range eventsResp.GetEvents() {
        if i >= 10 {
            break
        }
        fmt.Printf("  %s - %s\n", event.GetAction(), event.GetCreatedAt())
    }

    // Get workspace metrics
    metrics, _, err := client.MetricsAPI.MetricsServiceGetWorkspaceMetrics(ctx).Execute()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("\nWorkspace Stats:")
    fmt.Printf("  Total links: %d\n", metrics.GetTotalLinks())
    fmt.Printf("  Total clicks: %d\n", metrics.GetTotalClicks())
    fmt.Printf("  Total visitors: %d\n", metrics.GetTotalVisitors())
}

Framework integration

Gin

Go
package main

import (
    "context"
    "fmt"
    "net/http"
    "os"

    "github.com/gin-gonic/gin"
    "github.com/linkbreakers-com/linkbreakers-go"
)

func getClient() *linkbreakers.APIClient {
    config := linkbreakers.NewConfiguration()
    config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("LINKBREAKERS_SECRET_KEY")))
    config.Servers = linkbreakers.ServerConfigurations{
        {URL: "https://api.linkbreakers.com"},
    }
    return linkbreakers.NewAPIClient(config)
}

func main() {
    r := gin.Default()
    client := getClient()

    r.POST("/api/create-link", func(c *gin.Context) {
        var req struct {
            Destination string   `json:"destination" binding:"required"`
            Name        string   `json:"name"`
            Tags        []string `json:"tags"`
        }

        if err := c.ShouldBindJSON(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        createReq := linkbreakers.NewCreateLinkRequest(req.Destination)
        if req.Name != "" {
            createReq.SetName(req.Name)
        }
        if len(req.Tags) > 0 {
            createReq.SetTags(req.Tags)
        }

        response, _, err := client.LinksAPI.LinksServiceCreate(context.Background()).
            CreateLinkRequest(*createReq).
            Execute()

        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "success":   true,
            "shortlink": response.Link.GetShortlink(),
            "qr_code":   response.Link.GetQrcodeSignedUrl(),
        })
    })

    r.GET("/api/links", func(c *gin.Context) {
        tags := c.QueryArray("tags")
        pageSize := int32(50)

        response, _, err := client.LinksAPI.LinksServiceList(context.Background()).
            PageSize(pageSize).
            Tags(tags).
            Execute()

        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }

        var links []gin.H
        for _, link := range response.GetLinks() {
            links = append(links, gin.H{
                "id":          link.GetId(),
                "name":        link.GetName(),
                "shortlink":   link.GetShortlink(),
                "destination": link.GetDestination(),
            })
        }

        c.JSON(http.StatusOK, gin.H{"links": links})
    })

    r.Run(":8080")
}

Echo

Go
package main

import (
    "context"
    "fmt"
    "net/http"
    "os"

    "github.com/labstack/echo/v4"
    "github.com/linkbreakers-com/linkbreakers-go"
)

func getClient() *linkbreakers.APIClient {
    config := linkbreakers.NewConfiguration()
    config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("LINKBREAKERS_SECRET_KEY")))
    config.Servers = linkbreakers.ServerConfigurations{
        {URL: "https://api.linkbreakers.com"},
    }
    return linkbreakers.NewAPIClient(config)
}

type CreateLinkRequest struct {
    Destination string   `json:"destination"`
    Name        string   `json:"name"`
    Tags        []string `json:"tags"`
}

func main() {
    e := echo.New()
    client := getClient()

    e.POST("/api/create-link", func(c echo.Context) error {
        var req CreateLinkRequest
        if err := c.Bind(&req); err != nil {
            return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
        }

        createReq := linkbreakers.NewCreateLinkRequest(req.Destination)
        if req.Name != "" {
            createReq.SetName(req.Name)
        }
        if len(req.Tags) > 0 {
            createReq.SetTags(req.Tags)
        }

        response, _, err := client.LinksAPI.LinksServiceCreate(context.Background()).
            CreateLinkRequest(*createReq).
            Execute()

        if err != nil {
            return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
        }

        return c.JSON(http.StatusOK, map[string]interface{}{
            "success":   true,
            "shortlink": response.Link.GetShortlink(),
            "qr_code":   response.Link.GetQrcodeSignedUrl(),
        })
    })

    e.Logger.Fatal(e.Start(":8080"))
}

Error handling

Implement proper error handling for production:

Go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/linkbreakers-com/linkbreakers-go"
)

func createLinkWithErrorHandling(client *linkbreakers.APIClient) {
    ctx := context.Background()

    createReq := linkbreakers.NewCreateLinkRequest("https://example.com/page")
    createReq.SetName("My Link")

    response, httpResp, err := client.LinksAPI.LinksServiceCreate(ctx).
        CreateLinkRequest(*createReq).
        Execute()

    if err != nil {
        if httpResp != nil {
            log.Printf("API Error: Status %d\n", httpResp.StatusCode)
        }
        log.Fatalf("Error creating link: %v", err)
        return
    }

    fmt.Printf("Success: %s\n", response.Link.GetShortlink())
}

Frequently asked questions

Can I use the Go SDK with popular web frameworks?

Yes! The SDK is framework-agnostic and works seamlessly with Gin, Echo, Fiber, Chi, and any Go web framework.

How do I handle rate limits?

The SDK will return errors if you exceed rate limits. Implement exponential backoff retry logic for production applications.

Does the SDK support concurrency?

Yes, the SDK is safe for concurrent use. You can create multiple goroutines that share the same API client.

How do I get the LBID for visitor identification?

The LBID appears as a ?lbid= query parameter when visitors click your links with conversion tracking enabled. Extract it from request parameters and pass it to VisitorsServiceIdentify().

Can I create bulk links efficiently?

Yes, use LinksServiceCreateBulk() to create up to 1000 links in a single API call, which is much more efficient than individual requests.

What Go versions are supported?

The SDK supports Go 1.18+ with full support for generics and modern Go features.

How do I test without affecting production data?

Create test mode API keys from your dashboard. They operate on isolated test data separate from production analytics. You can identify test vs production keys by decoding the JWT and checking the environment claim.

Sources

Last reviewed

This article was last reviewed on March 31, 2025.

About the Author

LS

Laurent Schaffner

Founder & Engineer at Linkbreakers

Passionate about building tools that help businesses track and optimize their digital marketing efforts. Laurent founded Linkbreakers to make QR code analytics accessible and actionable for companies of all sizes.