Developer docs

Get started

Quickstart

In short

Make an API key in the Clikso dashboard. Then send one POST request with that key, and you get your new short link back. It works on every plan, including Free.

You need

An account
A Clikso account. The Free plan is fine.
A role
Admin in the workspace. Only admins can create API keys.
A terminal
Any terminal with curl. Or pick one of the eight other languages in the code tabs.

Create an API key

Sign in at app.clik.so. Open Settings, then the API tab, and press Generate New Key.

Give the key a name, such as "My script". Tick Write, which also gives Read. Then press Generate Key.

Copy the key now. It starts with clik_live_, and Clikso shows it only once.

Save the key in your terminal

Put the key in an environment variable. Every example in these docs reads it from there, so the key never sits in your code.

macOS and Linux
export CLIKSO_API_KEY="clik_live_your_key_here"
Windows PowerShell
$env:CLIKSO_API_KEY = "clik_live_your_key_here"

Create a short link

Send this request. Only destination_url is required. Leave out short_code and Clikso picks a random 7-character one.

curl -X POST "https://app.clik.so/api/links" \
  -H "Authorization: Bearer $CLIKSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_url": "https://example.com/summer-sale",
    "short_code": "summer-sale"
  }'
const res = await fetch("https://app.clik.so/api/links", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLIKSO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "destination_url": "https://example.com/summer-sale",
    "short_code": "summer-sale"
  }),
});

const data = await res.json();
console.log(res.status, data);

Uses fetch (Node 18+ or the browser). Set CLIKSO_API_KEY first.

import os
import requests

response = requests.post(
    "https://app.clik.so/api/links",
    headers={"Authorization": f"Bearer {os.environ['CLIKSO_API_KEY']}"},
    json={
        "destination_url": "https://example.com/summer-sale",
        "short_code": "summer-sale",
    },
)

print(response.status_code, response.json())

Uses requests. Set CLIKSO_API_KEY first.

<?php

$body = <<<'JSON'
{
  "destination_url": "https://example.com/summer-sale",
  "short_code": "summer-sale"
}
JSON;

$ch = curl_init('https://app.clik.so/api/links');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CLIKSO_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => $body,
]);

$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

echo $status, PHP_EOL, $response, PHP_EOL;

Uses the curl extension. Set CLIKSO_API_KEY first.

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	body := strings.NewReader(`{
  "destination_url": "https://example.com/summer-sale",
  "short_code": "summer-sale"
}`)

	req, err := http.NewRequest("POST", "https://app.clik.so/api/links", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("CLIKSO_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(res.StatusCode, string(data))
}

Uses net/http. Set CLIKSO_API_KEY first.

require "net/http"
require "json"

uri = URI('https://app.clik.so/api/links')
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("CLIKSO_API_KEY")}"
request["Content-Type"] = "application/json"
request.body = {
  'destination_url' => 'https://example.com/summer-sale',
  'short_code' => 'summer-sale'
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts response.code, response.body

Uses Net::HTTP. Set CLIKSO_API_KEY first.

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "destination_url": "https://example.com/summer-sale",
              "short_code": "summer-sale"
            }
            """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.clik.so/api/links"))
            .header("Authorization", "Bearer " + System.getenv("CLIKSO_API_KEY"))
            .header("Content-Type", "application/json")
            .method("POST", HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.statusCode());
        System.out.println(response.body());
    }
}

Uses java.net.http (Java 15+). Set CLIKSO_API_KEY first.

using System.Net.Http.Headers;

using var client = new HttpClient();
var request = new HttpRequestMessage(new HttpMethod("POST"), "https://app.clik.so/api/links");
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("CLIKSO_API_KEY"));
request.Content = new StringContent(
    """
    {
      "destination_url": "https://example.com/summer-sale",
      "short_code": "summer-sale"
    }
    """,
    System.Text.Encoding.UTF8,
    "application/json");

using var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());

Uses HttpClient (C# 11+). Set CLIKSO_API_KEY first.

#include <cstdlib>
#include <iostream>
#include <string>
#include <curl/curl.h>

static size_t collect(char* data, size_t size, size_t n, void* out) {
    static_cast<std::string*>(out)->append(data, size * n);
    return size * n;
}

int main() {
    CURL* curl = curl_easy_init();
    if (!curl) return 1;

    const char* key = std::getenv("CLIKSO_API_KEY");
    std::string auth = std::string("Authorization: Bearer ") + (key ? key : "");
    struct curl_slist* headers = curl_slist_append(nullptr, auth.c_str());
    headers = curl_slist_append(headers, "Content-Type: application/json");
    const char* body = R"json({
  "destination_url": "https://example.com/summer-sale",
  "short_code": "summer-sale"
})json";
    std::string response;

    curl_easy_setopt(curl, CURLOPT_URL, "https://app.clik.so/api/links");
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, collect);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    CURLcode rc = curl_easy_perform(curl);
    long status = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (rc != CURLE_OK) {
        std::cerr << curl_easy_strerror(rc) << std::endl;
        return 1;
    }
    std::cout << status << "\n" << response << std::endl;
    return 0;
}

Uses libcurl. Set CLIKSO_API_KEY first.

Response 201
{
  "status": 201,
  "success": true,
  "message": "Link created successfully",
  "data": {
    "id": "3f6c1a2e-9b4d-4e8f-a1c2-7d9e0f1a2b3c",
    "workspace_id": "8a1f2c3d-4b5e-4f60-9a7b-1c2d3e4f5a6b",
    "custom_domain_id": null,
    "domain": "clik.is",
    "domain_status": "default",
    "title": "",
    "description": "",
    "short_code": "summer-sale",
    "destination_url": "https://example.com/summer-sale",
    "status": "active",
    "expires_at": null,
    "expired_redirect_url": null,
    "folder_id": null,
    "ab_testing": [],
    "geo_targeting": null,
    "device_targeting": null,
    "qr_metadata": null,
    "og_preview": null,
    "meta_pixel_id": null,
    "tiktok_pixel_id": null,
    "ga4_measurement_id": null,
    "gtm_container_id": null,
    "has_password": false,
    "click_count": 0,
    "created_by": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "created_at": "2026-10-09T09:30:00.000Z",
    "updated_at": "2026-10-09T09:30:00.000Z",
    "tags": [],
    "creator": {
      "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
      "display_name": "Asha Rao",
      "email": "asha@example.com",
      "photo_url": ""
    }
  }
}

Open your short link

The API does not send back a full short URL. You build it from two fields: https:// + domain + / + short_code.

For the link above, that is https://clik.is/summer-sale. Open it, and it sends you to the destination.

What you get back

A 201 status and a JSON body. The body is the same shape for every call: status, success, message and data. Here data is your new link.

The fields you will use most:

  • id — the link's id. Use it to read, change or delete the link later.
  • domain and short_code — together they make the short URL.
  • destination_url — where the link sends people.
  • click_count — how many clicks the link has had so far.

Every field is listed on Create a link.

Common errors

401UNAUTHORIZEDThe key is missing or wrong. Check that CLIKSO_API_KEY is set in this terminal.Send Authorization: Bearer clik_live_… with a key from Settings → API.
403FORBIDDENThe key only has Read permission. Make a key with Write.Check the key’s permission and your role. If you send Workspace-Id, it must match the key’s workspace.
409CONFLICTSomeone already uses that short_code on that domain. Pick another, or leave it out.Pick another short_code, or leave it out to get a random one.
422VALIDATION_ERRORdestination_url is missing, or does not start with http:// or https://.Fix the field and send the request again.
429USAGE_LIMIT_REACHEDYou have made all the links your plan allows this month.Wait for the reset (analytics sends details.resets_at) or upgrade.

Good to know

  • The base URL for every call is https://app.clik.so/api.
  • A key belongs to one workspace. Links you make with it land in that workspace.
  • Never put a key in a web page or a mobile app. Anyone can read it there. See Keep your API key safe.
  • On the Free plan you can make 50 links a month and send 30 requests a minute. See Limits for every plan.

Next steps