URL Shortener API Tutorial: Automate Link Creation with Python and JavaScript
Hands-on tutorial for using Lnky's REST API to create, update and analyze short URLs programmatically — with code samples in Python, Node.js and curl.
If you're generating short URLs by hand in a dashboard, you're doing it wrong past about 20 links a month. The right move is to script it: a single API call creates the link, attaches metadata, and returns the short URL ready to ship into whatever workflow needs it.
This tutorial walks through the most common patterns using Lnky's REST API, with examples in Python, Node.js and curl.
Authentication
Lnky uses bearer token authentication. Generate a token from Settings → API Token → Generate. Store it as an environment variable — never commit it to a repo.
export LNKY_TOKEN="your-token-here"
Every request includes the token in an Authorization header:
Authorization: Bearer your-token-here
Endpoint summary
GET /api/shorten List your short URLs (returns a raw JSON array)
POST /api/shorten Create a new short URL
PATCH /api/shorten/{id} Update an existing short URL
DELETE /api/shorten/{id} Delete a short URL (204 on success)
All endpoints return JSON. Standard HTTP status codes apply (200, 201, 204, 400, 401, 403, 404, 422, 429).
The create/update endpoints accept a small set of fields: longUrl, password, expires_at (ISO 8601, must be in the future) and max_clicks. Branding, custom slugs, custom OG tags, tags and retargeting pixels are dashboard-only today.
Create a short URL
curl
curl -X POST https://lnky.click/api/shorten \
-H "Authorization: Bearer $LNKY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"longUrl": "https://example.com/very/long/path?with=params",
"expires_at": "2026-12-31T23:59:59Z",
"max_clicks": 1000
}'
Response:
{
"short_url": "https://lnky.click/aZb9Kp"
}
Python (requests)
import os
import requests
API_URL = "https://lnky.click/api/shorten"
TOKEN = os.environ["LNKY_TOKEN"]
def shorten(long_url, **kwargs):
payload = {"longUrl": long_url, **kwargs}
r = requests.post(
API_URL,
json=payload,
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=10,
)
r.raise_for_status()
return r.json()["short_url"]
# Usage
short = shorten(
"https://example.com/landing?utm_source=email",
expires_at="2026-12-31T23:59:59Z",
)
print(short)
Node.js (fetch)
const API_URL = "https://lnky.click/api/shorten";
const TOKEN = process.env.LNKY_TOKEN;
async function shorten(longUrl, opts = {}) {
const r = await fetch(API_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ longUrl, ...opts }),
});
if (!r.ok) throw new Error(`shorten failed: ${r.status}`);
const data = await r.json();
return data.short_url;
}
// Usage
const short = await shorten(
"https://example.com/landing?utm_source=email",
{ expires_at: "2026-12-31T23:59:59Z" }
);
console.log(short);
Common patterns
Pattern 1 — bulk-create campaign URLs
You have 50 product SKUs and want one branded short URL per product for a print campaign.
import csv
import os
API_URL = "https://lnky.click/api/shorten"
TOKEN = os.environ["LNKY_TOKEN"]
with open("products.csv") as f, open("short_urls.csv", "w") as out:
reader = csv.DictReader(f)
writer = csv.DictWriter(out, fieldnames=["sku", "destination", "short_url"])
writer.writeheader()
for row in reader:
destination = (
f"https://acme.com/products/{row['sku']}"
f"?utm_source=print&utm_campaign=q4&utm_content={row['sku']}"
)
short = shorten(destination)
writer.writerow({"sku": row["sku"], "destination": destination, "short_url": short})
Note: rate-limited to 60 requests per minute. For 1,000+ links, add a time.sleep(1) between calls or batch over multiple minutes.
Pattern 2 — list and update
Maybe you need to migrate all your short URLs to point at a new destination domain (old.com → new.com).
r = requests.get(API_URL, headers={"Authorization": f"Bearer {TOKEN}"})
links = r.json() # GET /api/shorten returns a raw JSON array, not {data: [...]}
for link in links:
if "old.com" in link["original_url"]:
new_url = link["original_url"].replace("old.com", "new.com")
requests.patch(
f"{API_URL}/{link['id']}",
json={"longUrl": new_url},
headers={"Authorization": f"Bearer {TOKEN}"},
)
print(f"Updated {link['id']}: -> {new_url}")
The short URL stays the same; only the destination changes. Live links update immediately.
Pattern 3 — programmatic links from a publishing workflow
A common SaaS pattern: every time you publish a blog post, automatically create a short URL for it (for Twitter sharing) with the post-specific UTM parameters baked in.
// In a publishing webhook handler:
async function onPostPublished(post) {
const destination = `${post.url}?utm_source=twitter&utm_medium=social&utm_campaign=blog-launch`;
const shortUrl = await shorten(destination);
await db.updatePost(post.id, { twitter_short_url: shortUrl });
}
Now your "share to Twitter" button uses the short URL with attribution baked in.
Pattern 4 — webhooks for click events (coming soon)
Lnky is working on webhook delivery for click events. Track this on the roadmap; today the recommended approach is to poll the API for click counts.
Rate limits
POST /api/shorten: 60 requests per minute per token.- All other endpoints: standard Laravel rate limit (
60/min).
A 429 response includes a Retry-After header. Respect it with backoff:
import time
import requests
def shorten_with_retry(long_url):
for attempt in range(3):
r = requests.post(API_URL, json={"longUrl": long_url}, headers={"Authorization": f"Bearer {TOKEN}"})
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", 5))
time.sleep(wait)
continue
r.raise_for_status()
return r.json()["short_url"]
raise RuntimeError("rate limit exhausted")
Error handling
| Status | Cause | Fix |
|---|---|---|
| 401 | Missing or invalid token | Regenerate from dashboard |
| 403 | Plan limit reached | Upgrade or wait for next month |
| 422 | Validation error (bad URL, slug taken, etc) | Check errors field in response body |
| 429 | Rate limited | Honor Retry-After, retry with backoff |
| 500 | Server error | Retry once; report if persistent |
Idempotency
The API does not deduplicate on longUrl — calling POST /api/shorten twice with the same URL produces two distinct short URLs. If you need idempotency (e.g. in a retry-prone job queue), store the short URL in your own DB keyed by the long URL.
Security
- Rotate tokens periodically. If your token leaks (committed to git, posted in a Slack screenshot), regenerate from the dashboard.
- Use one token per service. If you have a publishing pipeline, a CSV importer and a marketing automation tool all using the API, give each its own token. Revoking one doesn't affect the others.
- Don't expose the token in client-side code. All API calls should originate from your backend.
What's next
If you're integrating Lnky into a larger system, take a look at the admin panel (if you're on Agency) for usage monitoring across teammates, and the pricing page for the per-tier rate-limit details.
The full API reference is at /api-docs.
Subscribe to the Lnky Blog
Get new posts on link analytics, SEO and short-URL tactics — about one email a month.