Get started
Authentication
In short
Every request sends an API key in the Authorization header, as Bearer clik_live_…. A key belongs to one workspace. It acts as the person who made it, and it can never do more than that person can.
You need
- A plan
- Any plan. The API works on Free too.
- A role
- Admin in the workspace. Only admins can create, rename, revoke or delete keys.
- A safe place
- Somewhere to keep the key, such as an environment variable or a secrets manager.
Create a key
Open the API settings
Sign in at app.clik.so. Open Settings, then the API tab. Press Generate New Key.
Name the key and pick its permission
Give the key a name of up to 30 characters, such as "Order emails". The name helps you spot it later.
Then tick a permission. Write also gives Read, and Admin gives both. Press Generate Key.
Copy the key once
The Save your API key window shows the full key. This is the only time you will see it. Copy it now and store it safely.
A key looks like clik_live_ followed by 32 letters and digits.
Send the key
Put the key in the Authorization header of every request, after the word Bearer and a space.
Authorization: Bearer clik_live_your_key_hereThe X-API-Key header does not work. Only Authorization: Bearer does.
Check your key
This call reads your workspace's usage. Any key with Read permission can make it, so it is a good first test.
curl "https://app.clik.so/api/workspace/usage" \
-H "Authorization: Bearer $CLIKSO_API_KEY"const res = await fetch("https://app.clik.so/api/workspace/usage", {
headers: {
Authorization: `Bearer ${process.env.CLIKSO_API_KEY}`,
},
});
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.get(
"https://app.clik.so/api/workspace/usage",
headers={"Authorization": f"Bearer {os.environ['CLIKSO_API_KEY']}"},
)
print(response.status_code, response.json())Uses requests. Set CLIKSO_API_KEY first.
<?php
$ch = curl_init('https://app.clik.so/api/workspace/usage');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CLIKSO_API_KEY'),
],
]);
$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"
)
func main() {
req, err := http.NewRequest("GET", "https://app.clik.so/api/workspace/usage", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("CLIKSO_API_KEY"))
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"
uri = URI('https://app.clik.so/api/workspace/usage')
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("CLIKSO_API_KEY")}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.code, response.bodyUses 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 {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://app.clik.so/api/workspace/usage"))
.header("Authorization", "Bearer " + System.getenv("CLIKSO_API_KEY"))
.GET()
.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("GET"), "https://app.clik.so/api/workspace/usage");
request.Headers.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("CLIKSO_API_KEY"));
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());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "https://app.clik.so/api/workspace/usage");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
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.
{
"status": 200,
"success": true,
"message": "Success",
"data": {
"workspace_id": "8a1f2c3d-4b5e-4f60-9a7b-1c2d3e4f5a6b",
"links_created_this_month": 42,
"clicks_this_month": 1873,
"ai_analytics_today": 0,
"ai_slug_gens_today": 3,
"last_reset_ai": "2026-10-09",
"last_reset_monthly": "2026-10-01",
"next_reset": "2026-11-01T00:00:00.000Z",
"updated_at": "2026-10-09T09:30:00.000Z",
"plan_key": "starter",
"plan_links_limit": 500,
"plan_domains_limit": 2,
"is_simulated": false
}
}A 200 means the key works. A 401 means the key is wrong, revoked or missing.
What each permission allows
A key's permission sets the most it can do. Your own role in the workspace also counts. The key gets whichever of the two is lower.
- Read acts like a viewer. It can read links, folders, tags, domains, usage and click analytics.
- Write acts like an editor. It can do everything Read can, and also create, change and delete links, folders and tags.
- Admin is the same as Write for now. No API route needs more than Write today.
So an editor's Admin key still acts as an editor. And an admin's Read key can only read.
What keys can never do
Some parts of Clikso stay in the dashboard. A key that calls them gets 403 API_KEY_NOT_ALLOWED, whatever its permission.
- Create, list or revoke API keys.
- Change billing, members, invitations or workspace settings.
- Add, verify or delete custom domains. Keys can only read them.
- Reveal a link's password.
- Upload a social preview image, or use the AI analytics chat.
This keeps a leaked key from taking over your account.
One key, one workspace
Each key belongs to the workspace you made it in. Every link it creates lands there. To work with two workspaces, make one key in each.
You can also send a Workspace-Id header with the workspace id. You do not have to. But if you send it, it must match the key's workspace, or you get 403 FORBIDDEN. That stops a script from writing to the wrong place by mistake.
When people change
A key follows its creator, live, on every request.
- If the creator becomes a viewer, their Write key can only read.
- If the creator leaves the workspace, their keys stop working with
403 FORBIDDEN. - If the creator's account is suspended, every call gets
403 ACCOUNT_BANNED. - If the creator has not verified their email address, calls can get
403 EMAIL_NOT_VERIFIED.
To keep a script running when people change, have an admin who stays make its key.
What you get back
When the key is fine, you get the normal response for that endpoint. When it is not, you get an error with a code that tells you why. Every error has the same shape. See Requests and responses.
Common errors
UNAUTHORIZEDThe header is missing, or the key is mistyped, deleted or unknown.Send Authorization: Bearer clik_live_… with a key from Settings → API.Send Authorization: Bearer clik_live_… with a key from Settings → API.API_KEY_REVOKEDSomeone revoked the key in Settings → API.Create a new key and replace the old one.Create a new key and replace the old one.API_KEY_EXPIREDThe key had an expiry date, and it has passed.Create a new key.Create a new key.FORBIDDENThe permission or role is too low, or Workspace-Id names another workspace.Check the key’s permission and your role. If you send Workspace-Id, it must match the key’s workspace.Check the key’s permission and your role. If you send Workspace-Id, it must match the key’s workspace.API_KEY_NOT_ALLOWEDThis endpoint never accepts API keys.Use only the endpoints in the API reference.Use only the endpoints in the API reference.EMAIL_NOT_VERIFIEDThe person who made the key has not verified their email.Sign in to the dashboard and verify the email.Sign in to the dashboard and verify the email.RATE_LIMITEDToo many requests this minute, or too many failed key checks from your IP address.Wait the number of seconds in the Retry-After header, then try again.Wait the number of seconds in the Retry-After header, then try again.Good to know
- Your workspace can hold 3 keys on Free, 10 on Starter, 20 on Pro and 30 on Business. Revoked keys do not count; each browser connected to the Clikso extension holds one that does.
- The key limit only applies when you create a key. If you move to a smaller plan, the keys you have keep working.
- Each key has its own request limit per minute. See Limits.
- One IP address can fail 30 key checks a minute. Past that, its failed checks get
429 RATE_LIMITEDuntil the minute ends. - To change a key, make a new one, switch your code to it, then revoke the old one.
- Keep keys out of web pages, apps and Git. See Keep your API key safe.
