Let your team focus on product, not in-house licensing and distribution.

Easily add license key validation, entitlements, and device activation to your business's desktop apps, server applications, on-premise software, and other products using our software licensing API.


"Licensing was planned to make up at least 25% percent of planned dev time for our product so it's awesome to see that we've been able to cut it down to 2.5% with Keygen." – Ranorex

Free during development, no upfront commitment

5 stars
Demo of the Keygen admin dashboard
curl -X POST https://api.keygen.sh/v1/accounts/demo/licenses \
-H 'Authorization: Bearer prod-2ddd064509b6bcaa356958dcce6da3a5389189dfacav3' \
-H 'Content-Type: application/vnd.api+json' \
-H 'Accept: application/vnd.api+json' \
-d '{
"data": {
"type": "license",
"attributes": {
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3",
"expiry": "2024-10-31T00:00:00.000Z",
"metadata": {
"customerId": "cus_9lNHKnX65d6yvH"
}
},
"relationships": {
"policy": {
"data: {
"type": "policy",
"id": "0b4b1a9a-e25a-4f14-a95e-d9dd378d6065"
}
}
}
}
}'

Developer time is expensive

Making it more productive is a no-brainer

  • checkSay "no" to hidden maintenance costs. Skip the expensive up-front and ongoing costs of building an in-house licensing solution. Save your business development time and money with our ready-to-go software licensing and distribution API.
  • checkScale your operations. Effortlessly go from hundreds of users, to thousands, to hundreds of thousands and beyond. We'll handle the infrastructure so your team can focus on product.
  • checkGet to market faster. Quickly and securely integrate our ready-to-go cloud-based developer APIs into any software product, on any operating system.
Licensing in 2023: Build vs Buy

Bring your own programming language

Quickly implement Keygen's API in any programming language.

package main
 
import (
"github.com/denisbrodbeck/machineid"
"github.com/keygen-sh/keygen-go/v2"
)
 
func main() {
keygen.Account = "YOUR_KEYGEN_ACCOUNT_ID"
keygen.Product = "YOUR_KEYGEN_PRODUCT_ID"
keygen.LicenseKey = "key/..."
 
fingerprint, err := machineid.ProtectedID(keygen.Product)
if err != nil {
panic(err)
}
 
// Validate the license for the current fingerprint
license, err := keygen.Validate(fingerprint)
switch {
case err == keygen.ErrLicenseNotActivated:
// Activate the current fingerprint
machine, err := license.Activate(fingerprint)
switch {
case err == keygen.ErrMachineLimitExceeded:
panic("Machine limit has been exceeded!")
case err != nil:
panic("Machine activation failed!")
}
case err == keygen.ErrLicenseExpired:
panic("License is expired!")
case err != nil:
panic("License is invalid!")
}
 
fmt.Println("License is activated!")
}
const fetch = require('node-fetch')
 
const res = await fetch('https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key', {
method: 'POST',
headers: {
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json'
},
body: JSON.stringify({
meta: {
key: 'C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3'
}
})
})
 
const { meta } = await res.json()
 
if (meta.valid) {
// Do something
} else {
// Do something else
}
import SwiftyJSON
import Alamofire
 
Alamofire.request("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key",
method: .post,
headers: [
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json"
],
parameters: [
"meta": [
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
]
],
encoding: JSONEncoding.default
).responseJSON { response in
let json = JSON(data: response.data!)
let valid = json["meta"]["valid"].bool
 
if valid {
// Do something
} else {
// Do something else
}
}
using RestSharp;
using System;
using System.Collections.Generic;
 
var client = new RestClient("https://api.keygen.sh/v1/accounts/demo");
var request = new RestRequest(
"licenses/actions/validate-key",
Method.POST
);
 
request.AddHeader("Content-Type", "application/vnd.api+json");
request.AddHeader("Accept", "application/vnd.api+json");
request.AddJsonBody(new {
meta = new {
key = "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
});
 
var response = client.Execute<Dictionary<string, object>>(request);
var meta = (Dictionary<string, object>) response.Data["meta"];
 
if ((bool) meta["valid"])
{
// Do something
}
else
{
// Do something else
}
import com.mashape.unirest.http.exceptions.*
import com.mashape.unirest.http.*
import org.json.*
 
val body = JSONObject(mapOf(
"meta" to mapOf(
"key" to "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
)
))
 
val res = Unirest.post("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key")
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.body(body)
.asJson()
 
val data = res.getBody().getObject()
val meta = data.getJSONObject("meta")
 
if (meta.getBoolean("valid")) {
// Do something
} else {
// Do something else
}
import com.mashape.unirest.http.exceptions.*;
import com.mashape.unirest.http.*;
import org.json.*;
 
import static java.util.Map.ofEntries;
import static java.util.Map.entry;
 
JSONObject body = new JSONObject(ofEntries(
entry("meta", ofEntries(
entry("key", "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3")
))
));
 
HttpResponse<JsonNode> res = Unirest.post("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key")
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.body(body)
.asJson();
 
JSONObject data = res.getBody().getObject();
JSONObject meta = data.getJSONObject("meta");
 
if (meta.getBoolean("valid"))
{
// Do something
}
else
{
// Do something else
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let validation: serde_json::Value = reqwest::Client::new()
.post("https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key")
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.json(&serde_json::json!({
"meta": {
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
}))
.send()
.await?
.json()
.await?;
 
if validation["meta"]["valid"].as_bool().unwrap() {
// Do something
} else {
// Do something else
}
 
Ok(())
}
#include <iostream>
#include <string>
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
 
using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::json;
using namespace utility;
 
http_client client("https://api.keygen.sh/v1/accounts/demo");
http_request req;
 
value meta;
meta["key"] = value::string("C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3");
 
value body;
body["meta"] = meta;
 
req.headers().add("Content-Type", "application/vnd.api+json");
req.headers().add("Accept", "application/json");
 
req.set_request_uri("/licenses/actions/validate-key");
req.set_method(methods::POST);
req.set_body(body.serialize());
 
client.request(req)
.then([](http_response res)
{
auto data = res.extract_json().get();
auto meta = data.at("meta");
 
if (meta.at("valid").as_bool())
{
// Do something
}
else
{
// Do something else
}
})
.wait();
import requests
import json
 
data = requests.post(
"https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key",
headers={
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json"
},
data=json.dumps({
"meta": {
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
})
).json()
 
if data["meta"]["valid"]:
# Do something
else:
# Do something else
require 'httparty'
 
data = HTTParty.post(
'https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key',
headers: {
'Content-Type' => 'application/vnd.api+json',
'Accept' => 'application/vnd.api+json'
},
body: {
meta: {
key: 'C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3'
}
}
)
 
if data['meta']['valid']
# Do something
else
# Do something else
end
<?php
 
$url = 'https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key';
$ch = curl_init($url);
 
$body = json_encode([
'meta' => [
'key' => 'C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3'
]
]);
 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json'
]);
 
$json = curl_exec($ch);
$data = json_decode($json);
 
if ($data->meta->valid) {
// Do something
} else {
// Do something else
}

A better answer to Build vs. Buy

Businesses of all sizes face the question of whether to build or buy a product licensing solution. Keygen's developer APIs give you a better option that will save your business time and money.

  • check

    Reduce the up-front development and ongoing maintenance costs of an in-house solution.

  • check

    Maintain the flexibility of an in-house solution, but leave the hard stuff to us.

  • check

    Developer support and integration services by our licensing experts.

  • check

    Building blocks for implementing countless licensing models.

  • check

    Clear developer documentation covering how to integrate Keygen.


"Using Keygen saved me the trouble of building and maintaining my own licensing server and strategy. It is easy to work with, reliable, and affordable." – GuideGuide

Create an Account

"Saved time, money and made our customers happy. My staff are able to manage licenses with ease." – Anthony O.


FAQs

  • What licensing models are supported?

    Keygen supports almost any licensing model you can think of. From perpetual licenses, to timed licenses, to node-locked and floating activations models, to fine-grained entitlements. Our API can be configured to support your licensing requirements (and if it can't — we'll work with you to make sure that it can!)

  • Is data within Keygen secure?

    Yes. We take security very seriously and we strive to ensure that the data stored within Keygen is as secure as possible. All connections to our APIs are encrypted over TLS, i.e. we encrypt "in transit", our databases are encrypted, i.e. we encrypt "at rest", and all of our data backups are encrypted.

    Want to learn more? Check out our security page.

  • Where can I monitor Keygen's uptime?

    We strive to provide the highest uptime possible for our customers through a distributed system hosted on battle-tested infrastructure. You can monitor uptime and subscribe to updates on our status page.

  • Does Keygen handle distribution?

    Yes! Using Keygen Dist, you can distribute your software applications to licensed users and even provide automatic upgrades.

  • Does Keygen handle payments?

    No, but you can integrate our API server-side with payment providers, such as Stripe, or by using webhooks. For low- or no-code payment integrations, you can integrate Keygen with a payment provider using our Zapier integration.

  • Does Keygen work offline?

    Yes, we support several methods of cryptographically signing or encrypting license keys, which can be verified in offline environments. For more information, check out our API reference, or reach out to our technical support.

  • Can I ask a different question?

    Of course. Ask away.


"Keygen has flexible license management options backed by great customer support. Keygen is enabling us to very quickly get licensing off the ground for a reasonable price." – Mining & Metals