Skip to main content
This guide shows how to add API key verification to your Go applications using the official Unkey Go SDK.

Prerequisites

  • Go 1.21 or higher
  • An Unkey account (free at unkey.com)

1. Install the SDK

go get github.com/unkeyed/sdks/api/go/v2@latest

2. Set up your Unkey credentials

  1. Create an API in the Unkey Dashboard
  2. Create a root key at Settings → Root Keys
  3. Copy your API ID (looks like api_xxxx)
Set your root key as an environment variable:
export UNKEY_ROOT_KEY="unkey_xxxx"

3. Create middleware

Here’s how to verify API keys with 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
        }

        result := res.V2KeysVerifyKeyResponseBody.Data

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

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

func main() {
    mux := http.NewServeMux()
    
    // Protected route
    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))
}

4. Run your server

go run main.go
Test it with a valid API key:
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:8080/api/protected

Using with Gin

If you’re using the 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
        }

        result := res.V2KeysVerifyKeyResponseBody.Data

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

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

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

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

    r.Run(":8080")
}

Using with Echo

For the 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"})
            }

            result := res.V2KeysVerifyKeyResponseBody.Data

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

            // Store in context
            c.Set("keyId", *result.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 {
        keyId, ok := c.Get("keyId").(string)
        if !ok || keyId == "" {
            return c.JSON(http.StatusInternalServerError, map[string]string{
                "error": "Key ID not found in context",
            })
        }
        return c.JSON(http.StatusOK, map[string]string{
            "message": "Access granted",
            "key_id":  keyId,
        })
    }, UnkeyAuthMiddleware())

    e.Start(":8080")
}

What’s next?

Add rate limiting

Protect your endpoints from abuse

Go SDK Reference

Complete Go SDK documentation

Authorization

Add roles and permissions

Cookbook

More Go recipes and examples
Last modified on February 23, 2026