API reference / Links
List links
https://app.clik.so/api /linksIn short
limit and offset.You need
- API key
readpermission or higher. See Authentication.- Role
- Any member (viewer and up) in the key's workspace.
- Plan
- Any plan, including Free.
Example
curl "https://app.clik.so/api/links?limit=20" \
-H "Authorization: Bearer $CLIKSO_API_KEY"const res = await fetch("https://app.clik.so/api/links?limit=20", {
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/links",
headers={"Authorization": f"Bearer {os.environ['CLIKSO_API_KEY']}"},
params={
"limit": 20,
},
)
print(response.status_code, response.json())Uses requests. Set CLIKSO_API_KEY first.
<?php
$ch = curl_init('https://app.clik.so/api/links?limit=20');
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/links?limit=20", 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/links?limit=20')
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/links?limit=20"))
.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/links?limit=20");
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/links?limit=20");
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": [
{
"id": "3f6c1a2e-9b4d-4e8f-a1c2-7d9e0f1a2b3c",
"workspace_id": "8a1f2c3d-4b5e-4f60-9a7b-1c2d3e4f5a6b",
"custom_domain_id": null,
"domain": "clik.is",
"domain_status": "default",
"title": "Summer sale",
"description": "",
"short_code": "summer-sale",
"destination_url": "https://example.com/summer-sale",
"status": "active",
"expires_at": null,
"expired_redirect_url": null,
"folder_id": "6b7c8d9e-0f1a-4b2c-8d3e-4f5a6b7c8d9e",
"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": 128,
"created_by": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
"created_at": "2026-10-09T09:30:00.000Z",
"updated_at": "2026-10-09T09:30:00.000Z",
"tags": [
{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "launch",
"color": "#3b82f6"
}
],
"creator": {
"id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
"display_name": "Asha Rao",
"email": "asha@example.com",
"photo_url": ""
}
}
],
"meta": {
"total": 1,
"limit": 20,
"offset": 0,
"total_is_capped": false,
"has_more": false
}
}Query parameters
limitintegerHow many links to return. Defaults to 50, at most 200.
Default
50· Max 200offsetintegerHow many links to skip, for paging. Defaults to 0.
Default
0qstringSearch term, matched against the title, slug, destination URL and description.
folder_idstring · uuidOnly links in this folder.
statusstringOnly links with this status.
activeandexpiredalso look atexpires_at.One of:
active,archived,expiredtagsstringComma-separated tag ids. Returns links that have at least one of them.
sort_bystringField to sort by.
One of:
created_at,title,status,click_count· Defaultcreated_atorderstringSort direction.
One of:
desc,asc· Defaultdesc
What you get back
200 with the usual envelope — status, success, message, data. data is an array; each item has these fields:
idstring · uuidrequiredThe link's id.
workspace_idstring · uuidrequiredThe workspace the link belongs to.
custom_domain_idstring · uuid or nullThe custom domain's id, or
nullon a Clikso domain.domainstringrequiredThe link's domain, like
clik.isorlinks.example.com.domain_statusstringrequireddefaulton a Clikso domain,activeon a live custom domain,deletedwhen the custom domain was removed and the link no longer works.One of:
default,active,deletedtitlestringrequiredA name for the link. Empty string when not set.
Up to 60 characters
descriptionstringrequiredA note about the link. Empty string when not set.
Up to 250 characters
short_codestringrequiredThe part after the domain in the short URL: 3–30 letters, digits and hyphens, and on Starter and up
/for a nested code likeacme/launch(at most 5 parts). Up to 20 characters before 1.64.0.3–30 characters · Pattern
^(?:[a-zA-Z0-9-]+|-*[a-zA-Z0-9][a-zA-Z0-9-]*(?:/-*[a-zA-Z0-9][a-zA-Z0-9-]*){1,4})$destination_urlstring · urirequiredWhere the short link sends visitors.
statusstringrequiredactivelinks redirect.archivedandexpiredlinks do not.One of:
active,archived,expiredexpires_atstring · date-time or nullWhen the link stops working, or
nullfor never.expired_redirect_urlstring · uri or nullWhere visitors go after the link expires, or
nullfor Clikso's expired page.folder_idstring · uuid or nullThe folder the link is in, or
null.ab_testingarray of ABVariantA/B split destinations. An empty array when not set, never
null.ab_testing[].urlstring · urirequiredWhere this share of visitors goes.
ab_testing[].percentageintegerrequiredThis variant's weight. Weights are relative, so they do not have to add up to 100.
0–100
geo_targetingGeoTargeting or any or nullDestination per country, or
null.device_targetingDeviceTargeting or any or nullDestination per device, or
null.qr_metadataQrMetadata or any or nullSaved QR style, or
null.og_previewOgPreview or any or nullCustom social preview, or
null.meta_pixel_idstring or nullMeta Pixel id, or
null.tiktok_pixel_idstring or nullTikTok Pixel id, or
null.ga4_measurement_idstring or nullGoogle Analytics 4 measurement id, or
null.gtm_container_idstring or nullGoogle Tag Manager container id, or
null.has_passwordbooleanrequiredtruewhen visitors must enter a password. The password itself is never returned.click_countintegerrequiredLifetime clicks on this link.
Min 0
created_bystring · uuidThe user id of the member who created it.
created_atstring · date-timerequiredWhen the link was created.
updated_atstring · date-time or nullWhen the link last changed.
tagsarray of TagRefrequiredThe link's tags. An empty array when it has none.
tags[].idstring · uuidrequiredThe tag's id.
tags[].namestringrequiredThe tag's name.
Up to 10 characters
tags[].colorstring or nullrequiredThe tag's colour as
#rrggbb, or an empty string.creatorCreator or any or nullThe member who created the link, or
nullif unknown.meta.totalintegerrequiredHow many links match, up to 10,000.
0–10000
meta.limitintegerrequiredThe page size that was used.
meta.offsetintegerrequiredThe offset that was used.
meta.total_is_cappedbooleanrequiredtruewhen more than 10,000 links match andtotalstopped counting.meta.has_morebooleanrequiredtruewhen there is at least one more link after this page.
Common errors
UNAUTHORIZEDThe API key is missing, malformed or unknown.FORBIDDENThe Workspace-Id header names a different workspace than the key's.RATE_LIMITEDThe key went over its per-minute request limit.Every code and what to do about it: Errors.
Good to know
datais an array of links, and the paging info is inmeta, next todata.meta.totalstops counting at 10,000. Whenmeta.total_is_cappedistrue, page withmeta.has_moreinstead of comparing againsttotal.limitis at most 200. A bigger number is cut to 200, not refused.qsearches the title, slug, destination URL and description. It is a contains-match, not a full-text search.status=activeleaves out links whoseexpires_athas passed, even if their stored status still saysactive.tagstakes tag ids, comma-separated. A link matches if it has at least one of them.- Deleted links never appear. Archived links do, unless you filter by
status.
