Developer docs

API reference / Links

List links

GEThttps://app.clik.so/api/links

In short

Returns a page of your workspace's links, newest first. Filter by folder, status, tag or a search term, and page through with limit and offset.

You need

API key
read permission 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.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 {
        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.

Response 200
{
  "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
  }
}

Other responses: 401 · 403 · 429

Query parameters

  • limitinteger

    How many links to return. Defaults to 50, at most 200.

    Default 50 · Max 200

  • offsetinteger

    How many links to skip, for paging. Defaults to 0.

    Default 0

  • qstring

    Search term, matched against the title, slug, destination URL and description.

  • folder_idstring · uuid

    Only links in this folder.

  • statusstring

    Only links with this status. active and expired also look at expires_at.

    One of: active, archived, expired

  • tagsstring

    Comma-separated tag ids. Returns links that have at least one of them.

  • sort_bystring

    Field to sort by.

    One of: created_at, title, status, click_count · Default created_at

  • orderstring

    Sort direction.

    One of: desc, asc · Default desc

What you get back

200 with the usual envelope — status, success, message, data. data is an array; each item has these fields:

  • idstring · uuidrequired

    The link's id.

  • workspace_idstring · uuidrequired

    The workspace the link belongs to.

  • custom_domain_idstring · uuid or null

    The custom domain's id, or null on a Clikso domain.

  • domainstringrequired

    The link's domain, like clik.is or links.example.com.

  • domain_statusstringrequired

    default on a Clikso domain, active on a live custom domain, deleted when the custom domain was removed and the link no longer works.

    One of: default, active, deleted

  • titlestringrequired

    A name for the link. Empty string when not set.

    Up to 60 characters

  • descriptionstringrequired

    A note about the link. Empty string when not set.

    Up to 250 characters

  • short_codestringrequired

    The part after the domain in the short URL: 3–30 letters, digits and hyphens, and on Starter and up / for a nested code like acme/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 · urirequired

    Where the short link sends visitors.

  • statusstringrequired

    active links redirect. archived and expired links do not.

    One of: active, archived, expired

  • expires_atstring · date-time or null

    When the link stops working, or null for never.

  • expired_redirect_urlstring · uri or null

    Where visitors go after the link expires, or null for Clikso's expired page.

  • folder_idstring · uuid or null

    The folder the link is in, or null.

  • ab_testingarray of ABVariant

    A/B split destinations. An empty array when not set, never null.

  • ab_testing[].urlstring · urirequired

    Where this share of visitors goes.

  • ab_testing[].percentageintegerrequired

    This variant's weight. Weights are relative, so they do not have to add up to 100.

    0–100

  • geo_targetingGeoTargeting or any or null

    Destination per country, or null.

  • device_targetingDeviceTargeting or any or null

    Destination per device, or null.

  • qr_metadataQrMetadata or any or null

    Saved QR style, or null.

  • og_previewOgPreview or any or null

    Custom social preview, or null.

  • meta_pixel_idstring or null

    Meta Pixel id, or null.

  • tiktok_pixel_idstring or null

    TikTok Pixel id, or null.

  • ga4_measurement_idstring or null

    Google Analytics 4 measurement id, or null.

  • gtm_container_idstring or null

    Google Tag Manager container id, or null.

  • has_passwordbooleanrequired

    true when visitors must enter a password. The password itself is never returned.

  • click_countintegerrequired

    Lifetime clicks on this link.

    Min 0

  • created_bystring · uuid

    The user id of the member who created it.

  • created_atstring · date-timerequired

    When the link was created.

  • updated_atstring · date-time or null

    When the link last changed.

  • tagsarray of TagRefrequired

    The link's tags. An empty array when it has none.

  • tags[].idstring · uuidrequired

    The tag's id.

  • tags[].namestringrequired

    The tag's name.

    Up to 10 characters

  • tags[].colorstring or nullrequired

    The tag's colour as #rrggbb, or an empty string.

  • creatorCreator or any or null

    The member who created the link, or null if unknown.

  • meta.totalintegerrequired

    How many links match, up to 10,000.

    0–10000

  • meta.limitintegerrequired

    The page size that was used.

  • meta.offsetintegerrequired

    The offset that was used.

  • meta.total_is_cappedbooleanrequired

    true when more than 10,000 links match and total stopped counting.

  • meta.has_morebooleanrequired

    true when there is at least one more link after this page.

Common errors

401UNAUTHORIZEDThe API key is missing, malformed or unknown.
403FORBIDDENThe Workspace-Id header names a different workspace than the key's.
429RATE_LIMITEDThe key went over its per-minute request limit.

Every code and what to do about it: Errors.

Good to know

  • data is an array of links, and the paging info is in meta, next to data.
  • meta.total stops counting at 10,000. When meta.total_is_capped is true, page with meta.has_more instead of comparing against total.
  • limit is at most 200. A bigger number is cut to 200, not refused.
  • q searches the title, slug, destination URL and description. It is a contains-match, not a full-text search.
  • status=active leaves out links whose expires_at has passed, even if their stored status still says active.
  • tags takes 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.