Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/unkeyed/unkey/llms.txt

Use this file to discover all available pages before exploring further.

The official Unkey Go SDK provides full access to Unkey’s API for managing keys, verifying requests, and rate limiting in Go applications.

Installation

go get github.com/unkeyed/sdks/api/go/v2@latest
Requirements: Go 1.21 or higher

Initialization

Create a client with your root key:
package main

import (
    "os"
    unkey "github.com/unkeyed/sdks/api/go/v2"
)

func main() {
    client := unkey.New(
        unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    )
    
    // Use client...
}
Never expose your root key in client-side code or commit it to version control.

Verify API Keys

Validate a user’s API key:
package main

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

    unkey "github.com/unkeyed/sdks/api/go/v2"
    "github.com/unkeyed/sdks/api/go/v2/models/components"
)

func main() {
    ctx := context.Background()
    
    client := unkey.New(
        unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    )

    apiKey := "sk_live_..."  // From request header

    res, err := client.Keys.VerifyKey(ctx, components.V2KeysVerifyKeyRequestBody{
        Key: apiKey,
    })
    
    if err != nil {
        log.Fatalf("Verification failed: %v", err)
    }

    result := res.V2KeysVerifyKeyResponseBody
    
    if !result.Valid {
        fmt.Printf("Key invalid: %s\n", *result.Code)
        return
    }

    fmt.Println("Key is valid!")
    if result.OwnerID != nil {
        fmt.Printf("Owner: %s\n", *result.OwnerID)
    }
    if result.Credits != nil {
        fmt.Printf("Credits remaining: %d\n", *result.Credits)
    }
}

Verification Response

The response contains:
FieldTypeDescription
ValidboolWhether the key passed all checks
Code*stringStatus code (VALID, NOT_FOUND, RATE_LIMITED, etc.)
KeyID*stringThe key’s unique identifier
Name*stringHuman-readable name of the key
Metamap[string]anyCustom metadata
Expires*int64Unix timestamp (ms) when key expires
Credits*int64Remaining uses
Enabled*boolWhether the key is enabled
Roles[]stringRoles attached to the key
Permissions[]stringPermissions attached to the key
Identity*IdentityIdentity info
Ratelimits[]RatelimitRate limit states

HTTP Middleware

Protect your HTTP endpoints with authentication:

Standard Library (net/http)

package main

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

    unkey "github.com/unkeyed/sdks/api/go/v2"
    "github.com/unkeyed/sdks/api/go/v2/models/components"
)

var unkeyClient *unkey.Unkey

func init() {
    unkeyClient = unkey.New(
        unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    )
}

// AuthMiddleware verifies API keys on incoming requests
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Extract API key from header
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, `{"error": "Missing Authorization header"}`, http.StatusUnauthorized)
            return
        }

        apiKey := strings.TrimPrefix(authHeader, "Bearer ")

        // Verify with Unkey
        res, err := unkeyClient.Keys.VerifyKey(r.Context(), components.V2KeysVerifyKeyRequestBody{
            Key: apiKey,
        })

        if err != nil {
            http.Error(w, `{"error": "Verification service unavailable"}`, http.StatusServiceUnavailable)
            return
        }

        if !res.V2KeysVerifyKeyResponseBody.Valid {
            http.Error(w, `{"error": "Invalid API key"}`, http.StatusUnauthorized)
            return
        }

        // Add key info to context for handlers
        ctx := context.WithValue(r.Context(), "keyId", res.V2KeysVerifyKeyResponseBody.KeyID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func main() {
    mux := http.NewServeMux()
    
    mux.HandleFunc("/api/protected", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"message": "Access granted!"}`))   
    })

    // Wrap with auth middleware
    http.ListenAndServe(":8080", AuthMiddleware(mux))
}

Gin Framework

package main

import (
    "net/http"
    "os"
    "strings"

    "github.com/gin-gonic/gin"
    unkey "github.com/unkeyed/sdks/api/go/v2"
    "github.com/unkeyed/sdks/api/go/v2/models/components"
)

var unkeyClient *unkey.Unkey

func init() {
    unkeyClient = unkey.New(
        unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    )
}

func UnkeyAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing API key"})
            return
        }

        apiKey := strings.TrimPrefix(authHeader, "Bearer ")

        res, err := unkeyClient.Keys.VerifyKey(c.Request.Context(), components.V2KeysVerifyKeyRequestBody{
            Key: apiKey,
        })

        if err != nil {
            c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"error": "Verification failed"})
            return
        }

        if !res.V2KeysVerifyKeyResponseBody.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "Invalid API key",
                "code":  res.V2KeysVerifyKeyResponseBody.Code,
            })
            return
        }

        // Store verification result in context
        c.Set("unkeyResult", res.V2KeysVerifyKeyResponseBody)
        c.Next()
    }
}

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

    // Protected routes
    api := r.Group("/api", UnkeyAuth())
    {
        api.GET("/data", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"message": "Access granted"})
        })
    }

    r.Run(":8080")
}

Echo Framework

package main

import (
    "net/http"
    "os"
    "strings"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    unkey "github.com/unkeyed/sdks/api/go/v2"
    "github.com/unkeyed/sdks/api/go/v2/models/components"
)

var unkeyClient *unkey.Unkey

func init() {
    unkeyClient = unkey.New(
        unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    )
}

func UnkeyAuthMiddleware() echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            authHeader := c.Request().Header.Get("Authorization")
            if authHeader == "" {
                return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Missing API key"})
            }

            apiKey := strings.TrimPrefix(authHeader, "Bearer ")

            res, err := unkeyClient.Keys.VerifyKey(c.Request().Context(), components.V2KeysVerifyKeyRequestBody{
                Key: apiKey,
            })

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

            if !res.V2KeysVerifyKeyResponseBody.Valid {
                return c.JSON(http.StatusUnauthorized, map[string]string{
                    "error": "Invalid API key",
                    "code":  string(*res.V2KeysVerifyKeyResponseBody.Code),
                })
            }

            // Store in context
            c.Set("keyId", res.V2KeysVerifyKeyResponseBody.KeyID)
            return next(c)
        }
    }
}

func main() {
    e := echo.New()
    
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // Protected route
    e.GET("/api/protected", func(c echo.Context) error {
        return c.JSON(http.StatusOK, map[string]string{
            "message": "Access granted",
        })
    }, UnkeyAuthMiddleware())

    e.Start(":8080")
}

Create API Keys

Issue new keys for your users:
package main

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

    unkey "github.com/unkeyed/sdks/api/go/v2"
    "github.com/unkeyed/sdks/api/go/v2/models/components"
)

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

    client := unkey.New(
        unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    )

    // Optional values
    prefix := "sk_live"
    externalID := "user_123"
    name := "Production key"
    expires := time.Now().Add(30 * 24 * time.Hour).UnixMilli()
    remaining := int64(1000)

    res, err := client.Keys.CreateKey(ctx, components.V2KeysCreateKeyRequestBody{
        APIID:      "api_...",
        Prefix:     &prefix,
        ExternalID: &externalID,
        Name:       &name,
        Expires:    &expires,
        Credits: &components.KeyCreditsData{
            Remaining: &remaining,
        },
        Meta: map[string]any{
            "plan": "pro",
            "email": "user@example.com",
        },
    })

    if err != nil {
        log.Fatalf("Failed to create key: %v", err)
    }

    result := res.V2KeysCreateKeyResponseBody
    
    // Send this to your user - only time you'll see the full key!
    fmt.Printf("New key: %s\n", result.Key)
    fmt.Printf("Key ID: %s\n", result.KeyID)
}
The full API key is only returned once at creation. Unkey stores only a cryptographic hash.

Create Key with Auto-Refill

refillAmount := int64(1000)
refillInterval := components.RefillIntervalMonthly

res, err := client.Keys.CreateKey(ctx, components.V2KeysCreateKeyRequestBody{
    APIID:  "api_...",
    Prefix: &prefix,
    Credits: &components.KeyCreditsData{
        Remaining: &remaining,
        RefillInterval: &refillInterval,
        RefillAmount: &refillAmount,
    },
})

Create Key with Rate Limits

ratelimitName := "requests"

res, err := client.Keys.CreateKey(ctx, components.V2KeysCreateKeyRequestBody{
    APIID:  "api_...",
    Ratelimits: []components.CreateRatelimit{
        {
            Name:     &ratelimitName,
            Limit:    100,
            Duration: 60000, // milliseconds
        },
    },
})

Update Keys

Modify an existing key:
enabled := true
name := "Updated name"

_, err := client.Keys.UpdateKey(ctx, components.V2KeysUpdateKeyRequestBody{
    KeyID:   "key_...",
    Name:    &name,
    Enabled: &enabled,
    Meta: map[string]any{
        "plan": "enterprise",
    },
})

if err != nil {
    log.Fatalf("Failed to update key: %v", err)
}

Delete Keys

Permanently revoke a key:
_, err := client.Keys.DeleteKey(ctx, components.V2KeysDeleteKeyRequestBody{
    KeyID: "key_...",
})

if err != nil {
    log.Fatalf("Failed to delete key: %v", err)
}

Rate Limiting

Use the rate limit API directly:
res, err := client.Ratelimits.Limit(ctx, components.V2RatelimitsLimitRequestBody{
    Namespace:  "my-app",
    Identifier: "user_123",
    Limit:      100,
    Duration:   60000, // 60 seconds in milliseconds
})

if err != nil {
    log.Fatalf("Rate limit check failed: %v", err)
}

if !res.V2RatelimitsLimitResponseBody.Success {
    fmt.Printf("Rate limited. Reset at: %d\n", res.V2RatelimitsLimitResponseBody.Reset)
} else {
    fmt.Printf("Allowed. %d requests remaining\n", res.V2RatelimitsLimitResponseBody.Remaining)
}

Cost-Based Rate Limiting

cost := int64(10)  // Expensive operation

res, err := client.Ratelimits.Limit(ctx, components.V2RatelimitsLimitRequestBody{
    Namespace:  "my-app",
    Identifier: "user_123",
    Limit:      100,
    Duration:   60000,
    Cost:       &cost,
})

Error Handling

Handle errors with type assertions:
import "errors"

res, err := client.Keys.CreateKey(ctx, components.V2KeysCreateKeyRequestBody{
    APIID: "api_...",
})

if err != nil {
    // Check for specific error types
    var apiErr *components.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("API Error: %s - %s\n", apiErr.Code, apiErr.Message)
    } else {
        fmt.Printf("Unexpected error: %v\n", err)
    }
    return
}

// Safe to use result
fmt.Printf("Created key: %s\n", res.V2KeysCreateKeyResponseBody.Key)

Advanced Usage

Context with Timeout

import "time"

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

res, err := client.Keys.VerifyKey(ctx, components.V2KeysVerifyKeyRequestBody{
    Key: apiKey,
})

Custom HTTP Client

import (
    "net/http"
    "time"
)

httpClient := &http.Client{
    Timeout: 10 * time.Second,
}

client := unkey.New(
    unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
    unkey.WithClient(httpClient),
)

Concurrent Verification

import "sync"

func verifyKeys(keys []string) {
    var wg sync.WaitGroup
    
    for _, key := range keys {
        wg.Add(1)
        go func(k string) {
            defer wg.Done()
            
            res, err := client.Keys.VerifyKey(context.Background(), 
                components.V2KeysVerifyKeyRequestBody{
                    Key: k,
                })
            
            if err != nil {
                log.Printf("Error verifying %s: %v", k, err)
                return
            }
            
            if res.V2KeysVerifyKeyResponseBody.Valid {
                log.Printf("Key %s is valid", k)
            }
        }(key)
    }
    
    wg.Wait()
}

Complete Example: API Server

package main

import (
    "context"
    "encoding/json"
    "log"
    "net/http"
    "os"
    "strings"

    unkey "github.com/unkeyed/sdks/api/go/v2"
    "github.com/unkeyed/sdks/api/go/v2/models/components"
)

type Server struct {
    unkey *unkey.Unkey
}

func NewServer() *Server {
    return &Server{
        unkey: unkey.New(
            unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
        ),
    }
}

func (s *Server) AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "Missing Authorization header", http.StatusUnauthorized)
            return
        }

        apiKey := strings.TrimPrefix(authHeader, "Bearer ")

        res, err := s.unkey.Keys.VerifyKey(r.Context(), components.V2KeysVerifyKeyRequestBody{
            Key: apiKey,
        })

        if err != nil {
            log.Printf("Verification error: %v", err)
            http.Error(w, "Service unavailable", http.StatusServiceUnavailable)
            return
        }

        if !res.V2KeysVerifyKeyResponseBody.Valid {
            http.Error(w, "Invalid API key", http.StatusUnauthorized)
            return
        }

        ctx := context.WithValue(r.Context(), "keyId", res.V2KeysVerifyKeyResponseBody.KeyID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func (s *Server) HandleData(w http.ResponseWriter, r *http.Request) {
    keyId := r.Context().Value("keyId")
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]any{
        "message": "Access granted",
        "keyId":   keyId,
    })
}

func main() {
    server := NewServer()
    
    mux := http.NewServeMux()
    mux.HandleFunc("/api/data", server.HandleData)
    
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", server.AuthMiddleware(mux)))
}

GitHub Repository

Go SDK on GitHub

Complete auto-generated API reference and source code

Next Steps

Go Quickstart

Step-by-step integration guide

Rate Limiting

Protect endpoints from abuse

Authorization

Add roles and permissions

API Reference

REST API documentation

Build docs developers (and LLMs) love