Links

Update a link

Updates a link. Only the fields you send are changed. Send password: null to remove a password; send expiresAt: null to remove expiration. Destination changes on u2l.ai links re-run the trust check - untrusted destinations are rejected (move the link to another domain instead).

PATCH https://u2l.ai/api/v1/links/{domain}/{slug}

Authorizations

Authorizationstringrequired

Bearer authentication header of the form Bearer <token>, where <token> is your U2L API key (u2l_live_...). Create one in the dashboard under Settings > API.

Path Parameters

domainstringrequired

The link's domain (e.g. u.gy or u2l.ai).

slugstringrequired

The link's slug (the part after the domain).

Body

destinationstring

New destination URL.

titlestring
tagsstring[]
passwordstring

New password; send null to remove (Advanced plan or above).

expiresAtstring

New expiration; send null to remove.

permanentboolean
customOgobject

Custom social preview (Advanced plan or above).

customOg.titlestring
customOg.descriptionstring
customOg.imagestring

Image URL.

abTestobject

A/B testing config (Advanced plan or above).

abTest.enabledboolean
abTest.destinationBstring

Variant B destination URL.

abTest.splitPercentageinteger

Traffic percentage to destination A. Defaults to 50.

cloakingboolean

URL cloaking (Advanced+): serve the destination inside a frame so visitors keep seeing the short URL.

deepLinkboolean

Open the destination in its native mobile app (YouTube, Instagram, etc.) when the visitor has it installed.

routingobject

Traffic routing rules (Advanced+). Matched visitors are redirected to the rule URL instead of the base destination; rules override the A/B pick.

routing.geoobject

Uppercase ISO 3166-1 alpha-2 country code (or default) mapped to a destination URL. Max 20 rules.

routing.continentobject

Continent code (AF, AN, AS, EU, NA, OC, SA) mapped to a URL. A country rule beats a continent rule.

routing.deviceobject

Device type mapped to a destination URL. Keys: ios, android, mobile, desktop. Specific OS beats the generic mobile bucket.

routing.regionobject

Lowercase "region|cc" key using the region name (e.g. "karnataka|in") or the ISO 3166-2 code (e.g. "ka|in") mapped to a URL. Beats country rules. Max 20 rules.

routing.cityobject

Lowercase "city|cc" key (e.g. "bengaluru|in") mapped to a URL. Most specific; IP city detection is best-effort. Max 20 rules.

maxClicksinteger

Expire the link after this many human clicks (Growth+). Enforcement is eventual: the link stops within a few minutes of the limit; bot traffic never counts.

expiredUrlstring

Where visitors are redirected (302) once the link has expired by date or click limit. Unset shows the standard expired page.

startsAtstring

Before this time the link serves a not-active-yet page. Must be before expiresAt when both are set.

rotationobject

N-way weighted rotation (Advanced+). Visitors are split randomly across 2-10 target URLs by integer weights summing to 100. Supersedes the legacy abTest fields when set; analytics reports variants as A, B, C... by target order.

rotation.targetsobject[]
pixelsobject[]

Retargeting pixels (Advanced+). Fired from a brief redirect page for human visitors only.

pixels[].providerstringrequired

One of: meta, tiktok, gtm, google_ads, linkedin.

pixels[].idstringrequired

Vendor pixel/container/tag ID, e.g. a Meta Pixel ID or GTM-XXXX.

appFallbackstring

App deep links (Advanced+): 'store' sends mobile visitors without the domain's app installed to the App Store / Play Store (with the link carried in the Play install referrer). Requires a mobile app configured on the link's domain in the dashboard; visitors with the app installed open it directly via the domain's universal/app links. One of: store.

Response 200

idstring

Link ID (UUID).

slugstring

The part after the domain.

domainstring

The link's domain.

destinationstring

Where the link redirects to.

titlestring

Optional title.

tagsstring[]
hasPasswordboolean
expiresAtstring

Expiration time, if set.

permanentboolean

true = 301 redirect, false = 302.

typestring

SL = short link, QR = QR code, PG = link-in-bio page. One of: SL, QR, PG.

createdAtstring
updatedAtstring
folderIdstring
clicksinteger

Total clicks.

ogTitlestring

Custom social preview title.

ogDescriptionstring
ogImagestring
abTestEnabledboolean
destinationBstring

A/B variant destination.

splitPercentageinteger

Traffic percentage to destination A.

qrStyleobject

QR styling options (QR links only).

externalBrowserboolean

Open in the phone's default browser when tapped inside in-app browsers (Android).

cloakingboolean

URL cloaking (Advanced+): serve the destination inside a frame so visitors keep seeing the short URL.

deepLinkboolean

Open the destination in its native mobile app (YouTube, Instagram, etc.) when the visitor has it installed.

routingobject

Traffic routing rules (Advanced+). Matched visitors are redirected to the rule URL instead of the base destination; rules override the A/B pick.

routing.geoobject

Uppercase ISO 3166-1 alpha-2 country code (or default) mapped to a destination URL. Max 20 rules.

routing.continentobject

Continent code (AF, AN, AS, EU, NA, OC, SA) mapped to a URL. A country rule beats a continent rule.

routing.deviceobject

Device type mapped to a destination URL. Keys: ios, android, mobile, desktop. Specific OS beats the generic mobile bucket.

routing.regionobject

Lowercase "region|cc" key using the region name (e.g. "karnataka|in") or the ISO 3166-2 code (e.g. "ka|in") mapped to a URL. Beats country rules. Max 20 rules.

routing.cityobject

Lowercase "city|cc" key (e.g. "bengaluru|in") mapped to a URL. Most specific; IP city detection is best-effort. Max 20 rules.

maxClicksinteger

Expire the link after this many human clicks (Growth+). Enforcement is eventual: the link stops within a few minutes of the limit; bot traffic never counts.

expiredUrlstring

Where visitors are redirected (302) once the link has expired by date or click limit. Unset shows the standard expired page.

startsAtstring

Before this time the link serves a not-active-yet page. Must be before expiresAt when both are set.

clickCountinteger

Human clicks counted toward maxClicks. Present only when maxClicks is set.

rotationobject

N-way weighted rotation (Advanced+). Visitors are split randomly across 2-10 target URLs by integer weights summing to 100. Supersedes the legacy abTest fields when set; analytics reports variants as A, B, C... by target order.

rotation.targetsobject[]
pixelsobject[]

Retargeting pixels (Advanced+). Fired from a brief redirect page for human visitors only.

pixels[].providerstringrequired

One of: meta, tiktok, gtm, google_ads, linkedin.

pixels[].idstringrequired

Vendor pixel/container/tag ID, e.g. a Meta Pixel ID or GTM-XXXX.

appFallbackstring

App deep links (Advanced+): 'store' sends mobile visitors without the domain's app installed to the App Store / Play Store (with the link carried in the Play install referrer). Requires a mobile app configured on the link's domain in the dashboard; visitors with the app installed open it directly via the domain's universal/app links. One of: store.

Errors

400 The destination was rejected by safety or trust checks.
401 Missing or invalid API key.
403 Your plan does not include this feature, or a limit was reached.
404 The resource does not exist or is not yours.
429 Rate limit exceeded. Check X-RateLimit-Reset for when the window resets.

See Rate Limits & Errors for the error envelope and per-plan limits.

curl -X PATCH https://u2l.ai/api/v1/links/u.gy/my-link \
  -H "Authorization: Bearer u2l_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
  "destination": "https://example.com/new-page",
  "title": "Updated title"
}'
const response = await fetch("https://u2l.ai/api/v1/links/u.gy/my-link", {
  method: "PATCH",
  headers: {
    "Authorization": "Bearer u2l_live_your_api_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "destination": "https://example.com/new-page",
    "title": "Updated title"
  }),
});

const data = await response.json();
console.log(data);
import requests

response = requests.patch(
    "https://u2l.ai/api/v1/links/u.gy/my-link",
    headers={"Authorization": "Bearer u2l_live_your_api_key"},
    json={
      "destination": "https://example.com/new-page",
      "title": "Updated title"
    },
)

print(response.json())
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{
  "destination": "https://example.com/new-page",
  "title": "Updated title"
}`)
	req, _ := http.NewRequest("PATCH", "https://u2l.ai/api/v1/links/u.gy/my-link", body)
	req.Header.Set("Authorization", "Bearer u2l_live_your_api_key")
	req.Header.Set("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	data, _ := io.ReadAll(res.Body)
	fmt.Println(string(data))
}
{
  "id": "9f0c2f9e-3b7a-4c1d-8e5f-2a6b7c8d9e0f",
  "slug": "my-link",
  "domain": "u.gy",
  "destination": "https://example.com/new-page",
  "title": "Updated title",
  "tags": [
    "campaign"
  ],
  "hasPassword": false,
  "expiresAt": null,
  "permanent": true,
  "type": "SL",
  "createdAt": "2026-07-20T10:30:00.000Z",
  "updatedAt": "2026-07-21T08:12:00.000Z",
  "folderId": null,
  "clicks": 128
}