← Back to UnifiedLicensing

How to License Your Game: Complete Guide for Indie Developers

Published Aug 21, 2026 · 20 min read

Why Your Game Needs Licensing

You spent months, maybe years, building your game. Someone buys it, shares the download link, and suddenly 50 people are playing for free. Without licensing, you have no way to control who uses your software.

For indie developers, licensing isn't about being greedy. It's about:

What Is Game Licensing?

Game licensing is a system where players receive a unique key when they purchase your game, and your game validates that key before running. Here's the flow:

  1. Player buys your game — they receive a license key (like ABCD-1234-EFGH-5678)
  2. Player enters the key — your game sends it to a validation server
  3. Server validates — checks the key is real, not expired, not banned
  4. Game unlocks — player can now access the full game
  5. Ongoing checks — periodic re-validation ensures the key is still valid
Modern licensing goes further. You can lock a key to a specific device, offer time-limited trials, manage subscriptions, and even detect piracy attempts — all without annoying legitimate customers.

Licensing vs DRM vs Copy Protection

These terms get confused a lot. Here's the difference:

ApproachWhat It DoesProsCons
LicensingValidates a key before runningSimple, works offline with cachingKeys can be shared
DRMAlways-online verification (e.g., Denuvo)Harder to crackAnnoys legit users, expensive
Copy ProtectionObfuscates code, encrypts assetsSlows casual piracyDetermined pirates always win

For indie devs, licensing is the sweet spot. It's lightweight, works offline, and doesn't punish your real customers. Pair it with hardware fingerprinting and you've got solid protection.

Step-by-Step: How to License Your Game

Step 1: Choose a Licensing Provider

You can build your own system or use a service like UnifiedLicensing. For most indie devs, a service is faster and more secure.

Step 2: Create Your Product

Sign up, create a product, and get your API key and product key. On UnifiedLicensing, this takes about 2 minutes.

Step 3: Generate License Keys

You generate keys for your customers. This can be:

Step 4: Integrate the SDK

Add the licensing check to your game. The SDK handles validation, caching, and error handling.

Step 5: Validate on Launch

When a player starts your game, check their license key. If it's valid, let them play. If not, show the activation screen.

Step 6: Handle Offline

Players don't always have internet. Smart caching means a previously-validated key keeps working for days, even offline.

Licensing in Unity (C#)

Here's a complete license validation script for Unity:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class LicenseManager : MonoBehaviour
{
    private const string API_URL = "https://api.unifiedlicensing.com/api/v1/validate-license";
    private const string API_KEY = "ul_YOUR_API_KEY";
    private const string PRODUCT_KEY = "YOUR_PRODUCT_KEY";

    private string licenseKey;
    private bool isLicensed = false;

    void Start()
    {
        licenseKey = PlayerPrefs.GetString("LicenseKey", "");
        if (!string.IsNullOrEmpty(licenseKey))
        {
            StartCoroutine(ValidateLicense(licenseKey));
        }
        else
        {
            ShowActivationScreen();
        }
    }

    IEnumerator ValidateLicense(string key)
    {
        string json = JsonUtility.ToJson(new LicenseRequest
        {
            api_key = API_KEY,
            product_key = PRODUCT_KEY,
            license_key = key,
            machine_id = SystemInfo.deviceUniqueIdentifier,
            platform = "desktop"
        });

        using (UnityWebRequest req = new UnityWebRequest(API_URL, "POST"))
        {
            req.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(json));
            req.downloadHandler = new DownloadHandlerBuffer();
            req.SetRequestHeader("Content-Type", "application/json");

            yield return req.SendWebRequest();

            if (req.result == UnityWebRequest.Result.Success)
            {
                LicenseResponse response = JsonUtility.FromJson<LicenseResponse>(req.downloadHandler.text);
                if (response.valid)
                {
                    isLicensed = true;
                    OnLicensed();
                }
                else
                {
                    ShowError(response.error);
                }
            }
            else
            {
                // Offline? Check cache
                if (PlayerPrefs.GetInt("LastValid", 0) == 1)
                {
                    isLicensed = true;
                    OnLicensed();
                }
                else
                {
                    ShowError("Network error. Please check your connection.");
                }
            }
        }
    }

    void OnLicensed()
    {
        PlayerPrefs.SetString("LicenseKey", licenseKey);
        PlayerPrefs.SetInt("LastValid", 1);
        Debug.Log("License validated! Unlocking game...");
        // Unlock your game here
    }

    void ShowActivationScreen()
    {
        Debug.Log("Show license input UI");
    }

    void ShowError(string msg)
    {
        Debug.LogError("License error: " + msg);
    }

    [System.Serializable]
    class LicenseRequest
    {
        public string api_key;
        public string product_key;
        public string license_key;
        public string machine_id;
        public string platform;
    }

    [System.Serializable]
    class LicenseResponse
    {
        public bool valid;
        public string error;
        public string plan;
        public string expires_at;
    }
}

Licensing in Godot (GDScript)

extends Node

const API_URL = "https://api.unifiedlicensing.com/api/v1/validate-license"
const API_KEY = "ul_YOUR_API_KEY"
const PRODUCT_KEY = "YOUR_PRODUCT_KEY"

var is_licensed := false

func _ready():
    var saved_key = _load_key()
    if saved_key != "":
        _validate(saved_key)
    else:
        _show_activation()

func _validate(key: String):
    var http = HTTPRequest.new()
    add_child(http)
    http.request_completed.connect(_on_validation_complete)

    var body = JSON.stringify({
        "api_key": API_KEY,
        "product_key": PRODUCT_KEY,
        "license_key": key,
        "machine_id": OS.get_unique_id(),
        "platform": "desktop"
    })

    http.request(API_URL,
        ["Content-Type: application/json"],
        HTTPClient.METHOD_POST,
        body)

func _on_validation_complete(result, response_code, headers, body):
    if result == OK:
        var json = JSON.parse_string(body.get_string_from_utf8())
        if json and json.get("valid", false):
            is_licensed = true
            _on_licensed()
        else:
            _show_error(json.get("error", "Invalid key"))
    else:
        # Offline fallback
        if FileAccess.file_exists("user://license_valid.tmp"):
            is_licensed = true
            _on_licensed()
        else:
            _show_error("Network error")

func _on_licensed():
    var f = FileAccess.open("user://license_valid.tmp", FileAccess.WRITE)
    f.close()
    print("License validated! Starting game...")

func _show_activation():
    print("Show license input")

func _show_error(msg: String):
    print("Error: ", msg)

func _load_key() -> String:
    if FileAccess.file_exists("user://license.key"):
        var f = FileAccess.open("user://license.key", FileAccess.READ)
        return f.get_as_text().strip_edges()
    return ""

Licensing in Unreal Engine (C++)

// LicenseManager.h
#pragma once
#include "CoreMinimal.h"
#include "Http.h"

UCLASS()
class ULicenseManager : public UObject
{
    GENERATED_BODY()
public:
    void ValidateLicense(const FString& Key);
    DECLARE_DELEGATE_OneParam(FOnValidation, bool);
    FOnValidation OnValidation;
};

// LicenseManager.cpp
#include "LicenseManager.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"

void ULicenseManager::ValidateLicense(const FString& Key)
{
    TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
    Request->SetURL("https://api.unifiedlicensing.com/api/v1/validate-license");
    Request->SetVerb("POST");
    Request->SetHeader("Content-Type", "application/json");

    FString Json = FString::Printf(TEXT(
        "{\"api_key\":\"ul_YOUR_API_KEY\",\"product_key\":\"YOUR_PRODUCT_KEY\","
        "\"license_key\":\"%s\",\"machine_id\":\"%s\",\"platform\":\"desktop\"}"
    ), *Key, *FGenericPlatformMisc::GetDeviceId());

    Request->SetContentAsString(Json);
    Request->OnProcessRequestComplete().BindLambda(
        [this](FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bSuccess)
        {
            if (bSuccess && Resp.IsValid())
            {
                // Parse response and call OnValidation
            }
        });
    Request->ProcessRequest();
}

Licensing HTML5/Web Games

Web games are the simplest to license. Just add a validation call when the game loads:

const API = 'https://api.unifiedlicensing.com/api/v1';

async function checkLicense() {
    const key = localStorage.getItem('game_license');
    if (!key) return showActivation();

    const res = await fetch(`${API}/validate-license`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            api_key: 'ul_YOUR_API_KEY',
            product_key: 'YOUR_PRODUCT_KEY',
            license_key: key,
            machine_id: navigator.userAgent,
            platform: 'web'
        })
    });
    const data = await res.json();

    if (data.valid) {
        startGame();
    } else {
        localStorage.removeItem('game_license');
        showActivation();
    }
}

function activate() {
    const key = document.getElementById('license-input').value;
    localStorage.setItem('game_license', key);
    checkLicense();
}

checkLicense();

Common Mistakes to Avoid

  1. Requiring always-online — your players will rage when they can't play on an airplane. Cache validation results.
  2. Hardcoding API keys in plain text — obfuscate them or use environment variables.
  3. Not handling network failures — the validation server might be down. Gracefully degrade.
  4. Making activation painful — a 10-step activation process kills conversions. Keep it to one field.
  5. Forgetting mobile — phone validation is different from desktop. Use device-specific identifiers.
  6. No offline grace period — give players at least 7 days of offline play.
  7. Blocking modding communities — license your base game, not mods. Don't punish your community.

Monetization Models That Work With Licensing

ModelHow Licensing HelpsExample
One-time purchasePermanent key, machine-lockedSteam game, desktop app
SubscriptionMonthly key, expires when sub endsSaaS tool, cloud service
Free trialTime-limited key (7/14/30 days)"Try free for 14 days"
FreemiumBasic key free, premium key paidFree tier + Pro features
Pay-what-you-wantAny purchase generates a keyitch.io model

Ready to License Your Game?

UnifiedLicensing gives you license keys, SDK integration for every engine, and offline support — all on a free tier.

Get Started Free

Frequently Asked Questions

How much does game licensing cost?

UnifiedLicensing offers a free tier for indie developers. Paid plans start at $9/month for more keys and analytics. Most indie games never outgrow the free tier.

Can I use licensing with Steam or itch.io?

Yes. Steam has its own licensing, but you can add an extra layer with UnifiedLicensing for off-platform sales, direct sales, or beta testing.

Will licensing annoy my players?

Not if you do it right. One key entry on first launch, then automatic silent validation in the background. Most players never think about it.

What if my validation server goes down?

Smart caching means previously-validated keys keep working for 7+ days offline. Your players won't notice a brief outage.

Can I generate keys in bulk for a giveaway?

Yes. Generate 100, 500, or 1000 keys at once from the dashboard or via the API. Perfect for press kits and giveaways.