One short link can be created by hand. A thousand cannot. Invoice notifications, order tracking, personalised campaigns — all of them need link generation triggered from your own system.
Authentication
The API is not publicly open. You authenticate with HTTP Basic using a username (key_id) and password (secret) generated in the dashboard.
curl -u "lk_9f3c2a71b0e84d55:sk_XXXXXXXXXXXXXXXXXXXX" \
https://lnkz.tr/api/v1/me
The secret is shown only once at creation; only a hash is stored. Lose it and you generate a new key — that is a deliberate design choice.
Key hygiene
- Never hard-code the key; keep it in an environment variable.
- Generate a separate key per application. A leak then costs you one key, not all of them.
- Do not use the key client-side (browser JavaScript, mobile apps) — no secret stored there stays secret. Make the call from your own server.
Creating a single link
POST /api/v1/links
Content-Type: application/json
{
"url": "https://randevu.selfklinik.com/?GUID=a8faa905-477a-11f0-89df-52e18006e243",
"title": "Appointment link",
"tags": ["clinic", "booking"],
"expires_at": "2026-12-31 23:59:00",
"max_clicks": 5000
}
A successful call returns 201 with short_url, code and qr_url. Each success costs 1 credit; credits are refunded automatically on validation errors.
PHP
function shorten(string $url, array $extra = []): array
{
$ch = curl_init('https://lnkz.tr/api/v1/links');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_USERPWD => getenv('LNKZ_KEY_ID') . ':' . getenv('LNKZ_KEY_SECRET'),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['url' => $url] + $extra),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode((string) $response, true);
if ($status !== 201) {
throw new RuntimeException($data['error']['message'] ?? 'Could not create link');
}
return $data['link'];
}
Python
import os, requests
AUTH = (os.environ["LNKZ_KEY_ID"], os.environ["LNKZ_KEY_SECRET"])
def shorten(url, **extra):
response = requests.post(
"https://lnkz.tr/api/v1/links",
auth=AUTH,
json={"url": url, **extra},
timeout=15,
)
payload = response.json()
if response.status_code != 201:
raise RuntimeError(payload["error"]["message"])
return payload["link"]
Bulk generation
The endpoint creates one link per call. Three rules matter when processing tens of thousands of records.
1. Respect the rate limit
120 requests per minute per key. Exceed it and you get 429 with a Retry-After header. Work through a queue; do not fan out parallel requests and fight the limit.
import time
def shorten_with_retry(url, attempts=5, **extra):
for attempt in range(attempts):
try:
return shorten(url, **extra)
except RuntimeError:
raise # validation error: retrying is pointless
except requests.HTTPError as e:
if e.response.status_code == 429:
time.sleep(int(e.response.headers.get("Retry-After", 60)))
continue
raise
raise RuntimeError("rate limit exceeded")
2. Distinguish retryable errors
| Code | Meaning | What to do |
|---|---|---|
| 401 | Invalid key | Do not retry; fix configuration |
| 402 | Not enough credits | Stop, alert, top up |
| 422 | Invalid field or code taken | Fix the data; the same request will fail again |
| 429 | Rate limited | Wait for Retry-After, then retry |
| 5xx | Server error | Retry with exponential backoff |
3. Store the result on your side
Write the returned code into your own database. It prevents processing the same record twice (and spending credits twice). Simple rule: if your record already has a short_code, never call the API for it.
Budgeting credits
Check your balance before starting:
GET /api/v1/me
→ { "account": { "credits": 4820 }, "costs": { "link": 1, "ai_slug": 5 } }
A 50,000-link send needs 50,000 credits. Running out mid-job leaves a half-finished send — calculate and top up before bulk work.
Meaningful codes with AI
For human-facing links, readable codes outperform random ones. The AI endpoint analyses the destination and proposes candidates:
POST /api/v1/ai/suggest
{ "task": "slug", "url": "https://example.com/campaigns/summer-sale-2026" }
→ {
"result": {
"suggestions": [
{ "code": "summer-sale", "reason": "The campaign name is directly recognisable" },
{ "code": "summer26-deal", "reason": "Also states the period" }
]
},
"credits_charged": 5
}
At 5 credits per call this is not economical per record in bulk. Use it where links are singular and human-facing — campaigns, products, categories — and leave random codes on the thousands of per-recipient links.
Checklist
- Keep the key in an environment variable, one per application.
- Set a timeout on every request (15 s is plenty).
- Honour
Retry-Afteron 429; never retry other 4xx responses. - Persist the generated code and guard against duplicate creation.
- Verify your credit balance before a bulk run.
- Set
expires_atandmax_clickson per-recipient links. - Test with 10 records before running the full list.