# Requests and responses

The Clikso API takes JSON and returns JSON in one envelope. Learn the base URL, the headers, how updates work, and how to page through long lists.

> **In short:**
>
> Send JSON to `https://app.clik.so/api` with your key in the `Authorization` header. Every answer comes back in the same JSON envelope, with your data in `data`. Long lists come in pages.

## You need

* **An API key:** See [Authentication](https://clik.so/docs/authentication).
* **A server:** Your own backend, script or job. The API does not answer calls from a web page.

## Base URL

Every path in these docs comes after this base URL:

```text title="Base URL"
https://app.clik.so/api
```

So `GET /links` means `GET https://app.clik.so/api/links`. Always use `https`.

## Headers

* `Authorization: Bearer clik_live_…` — on every request. See [Authentication](https://clik.so/docs/authentication).
* `Content-Type: application/json` — on every request that has a body.
* `Workspace-Id: <workspace id>` — optional. If you send it, it must match the key's workspace.

## Response envelope

Every success with a body looks like this. Your data is in `data`. The `status` field repeats the HTTP status.

Every error looks like this. Read `error.code` in your code, not `error.message`. The message is for people, and its words can change.

Some errors add an `error.details` object with more facts. The [Errors](https://clik.so/docs/errors) page shows each one.

### Empty responses

A delete answers `204 No Content` with an empty body. Do not try to parse it as JSON. A `204` means it worked.

### Ids and dates

* Every id is a UUID, such as `3f6c1a2e-9b4d-4e8f-a1c2-7d9e0f1a2b3c`.
* Every date is an ISO 8601 string in UTC, such as `2026-10-09T09:30:00.000Z`.
* When you send a date, such as `expires_at`, use the same format.

## Change things with PUT

To change a link, folder or tag, send `PUT` with only the fields you want to change. Fields you leave out stay as they are. There is no `PATCH` for single items.

```bash
curl -X PUT "https://app.clik.so/api/links/3f6c1a2e-9b4d-4e8f-a1c2-7d9e0f1a2b3c" \
  -H "Authorization: Bearer $CLIKSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_url": "https://example.com/autumn-sale",
    "title": "Autumn sale",
    "expires_at": null
  }'
```

Response 200:

```json
{
  "status": 200,
  "success": true,
  "message": "Link updated",
  "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": "Autumn sale",
    "description": "",
    "short_code": "summer-sale",
    "destination_url": "https://example.com/autumn-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-10T08:15: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": ""
    }
  }
}
```

Three rules for update bodies:

* A field you leave out does not change.
* A field set to `null` is cleared. Above, `expires_at: null` removes the expiry date. The rule objects work the same way: `geo_targeting: null` removes the country rules. Sending `{}` (or `[]` for `ab_testing`) clears them too.
* On links, `tags` is the one exception. `tags: null` leaves the tags alone. Send `tags: []` to remove every tag.

To change many links at once, use `PATCH /links/bulk` or `DELETE /links/bulk`. See [Manage links](https://clik.so/docs/guides/manage-links).

## Page through lists

List calls return one page at a time. You choose the page with `limit` and `offset`.

```bash
curl "https://app.clik.so/api/links?limit=20" \
  -H "Authorization: Bearer $CLIKSO_API_KEY"
```

Response 200:

```json
{
  "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
  }
}
```

The `meta` object next to `data` tells you where you are:

* `limit` — how many items you asked for. The default is 50. The most is 200; a bigger number is cut down, not refused.
* `offset` — how many items were skipped.
* `has_more` — `true` when there is at least one more page.
* `total` — how many items match. It stops counting at 10,000.
* `total_is_capped` — `true` when `total` stopped at that cap.

To read every link, add `limit` to `offset` and ask again until `has_more` is `false`. Do not stop at `total`, because it may be capped.

### Search and sort

* `q` searches the title, short code, destination URL and description.
* `sort_by` picks the order: `created_at` (the default), `title`, `status` or `click_count`.
* `order` is `desc` (newest first, the default) or `asc`.

Folders and tags come back as one full list, sorted by name. They have no paging.

## Call from your server

The API does not send CORS headers, so a browser blocks calls from a web page. That is on purpose: a key in a web page is a key anyone can copy. Call the API from your server, then pass the result to your page.

## What you get back

* `200` with the envelope for reads and updates.
* `201` with the envelope when you create something.
* `204` with no body when you delete something.
* `4xx` or `5xx` with the error envelope when something goes wrong.

## Common errors

| Status | Code               | What it means                                                       | What to do                                                                                               |
| ------ | ------------------ | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| 401    | `UNAUTHORIZED`     | The `Authorization` header is missing or the key is wrong.          | Send `Authorization: Bearer clik_live_…` with a key from Settings → API.                                 |
| 403    | `FORBIDDEN`        | The `Workspace-Id` header names a different workspace than the key. | Check the key’s permission and your role. If you send `Workspace-Id`, it must match the key’s workspace. |
| 404    | `NOT_FOUND`        | The id is not in this workspace, or it was deleted.                 | Check the id. Ids from another workspace are never found.                                                |
| 422    | `VALIDATION_ERROR` | The body is not valid JSON, or a field is missing or wrong.         | Fix the field and send the request again.                                                                |
| 429    | `RATE_LIMITED`     | Too many requests this minute. Wait for the `Retry-After` seconds.  | Wait the number of seconds in the `Retry-After` header, then try again.                                  |

## Good to know

* New fields can appear in responses at any time. Ignore fields you do not know, and never fail on them.
* `message` is for people. Your code should check `success` and `error.code`.
* The API never returns a ready-made short URL. Build it from `domain` and `short_code`: `https://clik.is/summer-sale`.
* There is no bulk create. Send one `POST /links` per link.

---

Source: https://clik.so/docs/requests
