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:
go get github.com/linkbreakers-com/linkbreakers-go
The SDK will be automatically added to your go.mod file:
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:
- Log in to app.linkbreakers.com
- Navigate to Dashboard β API Tokens
- Click Create API Token
- Select Secret Key (full API access)
- Give it a descriptive name (e.g., "Production Backend" or "Go Service")
- 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:
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:
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:
# .env file (use godotenv to load)
export LINKBREAKERS_SECRET_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ3b3Jrc3BhY2VfaWQiOiIxMjM0IiwidHlwZSI6InNlY3JldCJ9...
Loading .env files with godotenv:
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")
}
}
Creating and managing links
Basic link creation
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())
}
Advanced link options
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())
}
Listing and filtering links
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())
}
}
Updating and deleting links
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)
}
}
Bulk link creation
Create hundreds of links efficiently in a single API call:
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:
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
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:
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
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
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:
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
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.
Related Articles
How to use the Linkbreakers API
Complete guide to integrating with the Linkbreakers API - create QR codes, manage links, customize designs, track analytics, and automate workflows programmatically.
Analytics API
Access comprehensive QR code and visitor analytics through the Linkbreakers API. Learn how to retrieve campaign performance data, visitor insights, and engagement metrics programmatically for business intelligence integration.
How to integrate Linkbreakers with existing tech stack
Integrate Linkbreakers with your CRM, marketing automation, analytics platforms, and business systems through APIs, webhooks, and direct integrations. Learn best practices for seamless tech stack integration.
On this page
Need more help?
Can't find what you're looking for? Get in touch with our support team.
Contact Support