# Link expiry

Make a Clikso short link stop working at a set date and time, and choose where visitors go after it expires, using the API.

> **In short:**
>
> Set `expires_at` on a link, and from that moment the short URL stops sending people to the destination. Add `expired_redirect_url` to send late visitors somewhere useful instead. Expiry works on every plan, including Free.

## You need

* **API key:** A key with **Write** permission.
* **Role:** Editor or admin in the key’s workspace.
* **Plan:** Any plan, including Free.

## Set an expiry date

Send `expires_at` as an ISO 8601 date and time. Add `Z` at the end to mean UTC, so there is no doubt about the time zone.

```bash
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",
    "expires_at": "2026-12-31T23:59:00Z",
    "expired_redirect_url": "https://example.com/sale-ended"
  }'
```

You get the new link back. Its `expires_at` is the same moment, written as `2026-12-31T23:59:00.000Z`.

`expired_redirect_url` is optional. It must be a full `http://` or `https://` URL, up to 2,048 characters.

## Change or remove the expiry

To move the date, update the link with a new `expires_at`:

```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 '{
    "expires_at": "2027-01-31T23:59:00Z"
  }'
```

To remove the expiry, send `null`. The link then works with no end date.

```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 '{
    "expires_at": null
  }'
```

`expired_redirect_url: null` removes the redirect for late visitors in the same way.

## What visitors see

Before `expires_at`, the short link works as usual.

After it, a visitor is sent to `expired_redirect_url` if you set one. If you did not, they land on a "link expired" page on the same domain. That page answers with `410 Gone`, so search engines drop the link.

Clikso checks the date on every visit. Nothing has to run on your side.

## Find expired links

Filter the link list with `status=expired`:

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

This also finds links whose `expires_at` has passed but whose stored `status` still says `active`. In the same way, `status=active` leaves those links out.

## Expire or archive?

They both stop a link, but they do different jobs:

* **Expire** — set a date in advance. The link stops by itself at that moment.
* **Archive** — stop the link now, by hand. Send `status: "archived"` in an update.

An archived link answers like a link that does not exist, with a "not found" page. To turn it back on, send `status: "active"`.

```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 '{
    "status": "archived"
  }'
```

To bring back an expired link, send `status: "active"` with a new `expires_at` in the future, or with `expires_at: null`.

## What you get back

A `200` (or `201` for a new link) with the whole link in `data`. These fields matter here:

* `expires_at` — the expiry moment in UTC, or `null`.
* `expired_redirect_url` — where late visitors go, or `null`.
* `status` — `active`, `archived` or `expired`.

## Common errors

| Status | Code                  | What it means                                                                                                                                          | What to do                                                                                               |
| ------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| 403    | `FORBIDDEN`           | The key has only Read permission, or its owner is a viewer.                                                                                            | Check the key’s permission and your role. If you send `Workspace-Id`, it must match the key’s workspace. |
| 404    | `NOT_FOUND`           | No link with that id in this workspace.                                                                                                                | Check the id. Ids from another workspace are never found.                                                |
| 422    | `VALIDATION_ERROR`    | `expires_at` is not a date, `expired_redirect_url` is too long, or `status` is not `active`, `archived` or `expired`. `details.field` names the field. | Fix the field and send the request again.                                                                |
| 422    | `DESTINATION_BLOCKED` | `expired_redirect_url` points at another link shortener or a blocked site.                                                                             | Show `error.message` to the user as it is. Do not retry.                                                 |

## Good to know

* A link has one `expires_at`. There are no repeating windows.
* There is no "expire after a number of clicks". Expiry is by date and time only.
* Send a real date in ISO 8601, like `2026-12-31T23:59:00Z`. Anything else gets `422 VALIDATION_ERROR`.
* A date in the past is accepted. The link is then expired from the start.
* Clikso stores an exact moment, not a local clock time. A teammate in another time zone sees the same moment.
* Expired and archived links still count toward the monthly link limit. Their clicks stay in your analytics.

---

Source: https://clik.so/docs/guides/expiry
