Link Management API
Create intelligent links that optimize themselves. Build A/B tests, geo-routed destinations, and conversion-tracking campaigns through the Klinky API.
Start building smarter links — Create your first A/B test in minutes
What You Can Build
The Links API powers sophisticated link strategies:
- A/B Test Links — Split traffic between variants and measure conversion rates
- Geo-Routed Links — Send visitors to location-appropriate destinations
- Auto-Optimizing Links — Let algorithms detect winners and adjust traffic automatically
- Conversion-Tracked Links — Connect clicks to meaningful business outcomes
Create Link
Create a new link with multiple variants for A/B testing or geo-routing.
Endpoint: POST /public/links
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Descriptive name (1-100 chars) |
| slug | string | No | Custom short URL path (3-32 chars). Auto-generated if omitted. |
| variants | array | Yes | Array of 2-5 variants. Weights must sum to 100. |
| auto_winner | boolean | No | Enable automatic winner detection. Default: false. |
| auto_winner_threshold | integer | No | Minimum clicks before auto-winner. Default: 100, min: 10. |
| routing_type | string | No | ab_test or geo. Default: ab_test. |
| geo_rules | object | No | Country-to-variant mapping for geo routing. |
Variant Object
| Field | Type | Required | Description |
|---|---|---|---|
| label | string | Yes | Unique identifier (1-50 chars) |
| destination_url | string | Yes | Target URL (max 2048 chars) |
| weight | integer | Yes | Traffic percentage (0-100) |
Simple A/B Test Example
curl -X POST https://klinky-api.fly.dev/api/v1/public/links \
-H "X-API-Key: klinky_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Homepage Headline Test",
"slug": "homepage-test",
"variants": [
{
"label": "control",
"destination_url": "https://example.com/control",
"weight": 50
},
{
"label": "variant_b",
"destination_url": "https://example.com/variant-b",
"weight": 50
}
]
}'Geo-Routing Example
Route visitors to different destinations based on their country — perfect for regional stores, language localization, and compliance:
curl -X POST https://klinky-api.fly.dev/api/v1/public/links \
-H "X-API-Key: klinky_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Regional Landing Pages",
"slug": "regional",
"routing_type": "geo",
"variants": [
{
"label": "us_page",
"destination_url": "https://example.com/us",
"weight": 40
},
{
"label": "eu_page",
"destination_url": "https://example.com/eu",
"weight": 30
},
{
"label": "default_page",
"destination_url": "https://example.com/global",
"weight": 30
}
],
"geo_rules": {
"US": "us_page",
"GB": "us_page",
"DE": "eu_page",
"FR": "eu_page",
"default": "default_page"
}
}'Geo Rules Requirements:
- Use ISO 3166-1 alpha-2 country codes (e.g.,
US,GB,DE) - Must include a
defaultvariant for unmatched countries - All variant labels in geo_rules must exist in the variants array
Learn more about geo-routing — See use cases and best practices
Auto-Winner Detection Example
Automatically declare a winner when one variant significantly outperforms others:
curl -X POST https://klinky-api.fly.dev/api/v1/public/links \
-H "X-API-Key: klinky_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Launch Test",
"slug": "product-launch",
"auto_winner": true,
"auto_winner_threshold": 100,
"variants": [
{
"label": "version_a",
"destination_url": "https://example.com/a",
"weight": 50
},
{
"label": "version_b",
"destination_url": "https://example.com/b",
"weight": 50
}
]
}'When enabled, Klinky monitors conversion rates and automatically directs 100% of traffic to the winning variant once:
- At least
auto_winner_thresholdclicks have been recorded - One variant has a statistically significant higher conversion rate
See auto-winner best practices — Choose the right threshold for your conversion rate
Response (201 Created)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "660e8400-e29b-41d4-a716-446655440000",
"organization_id": null,
"slug": "homepage-test",
"name": "Homepage Test",
"is_active": true,
"auto_winner": false,
"auto_winner_threshold": 100,
"routing_type": "ab_test",
"geo_rules": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"variants": [
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"link_id": "550e8400-e29b-41d4-a716-446655440000",
"label": "control",
"destination_url": "https://example.com/control",
"weight": 50,
"is_winner": false,
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": "880e8400-e29b-41d4-a716-446655440000",
"link_id": "550e8400-e29b-41d4-a716-446655440000",
"label": "variant_b",
"destination_url": "https://example.com/variant-b",
"weight": 50,
"is_winner": false,
"created_at": "2024-01-15T10:30:00Z"
}
]
}Your link is now live at https://klinky.io/homepage-test
List Links
Retrieve a paginated list of all your links with basic statistics.
Endpoint: GET /public/links
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| per_page | integer | 20 | Items per page (max 100) |
Example
curl "https://klinky-api.fly.dev/api/v1/public/links?page=1&per_page=20" \
-H "X-API-Key: klinky_live_your_api_key_here"Response (200 OK)
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "homepage-test",
"name": "Homepage Test",
"is_active": true,
"routing_type": "ab_test",
"created_at": "2024-01-15T10:30:00Z",
"total_clicks": 1523,
"total_conversions": 127,
"top_variant": "variant_b"
},
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"slug": "regional",
"name": "Regional Landing Pages",
"is_active": true,
"routing_type": "geo",
"created_at": "2024-01-14T09:15:00Z",
"total_clicks": 892,
"total_conversions": 45,
"top_variant": "us_page"
}
],
"total": 42
}Get Link Details
Retrieve detailed information about a specific link including all variants.
Endpoint: GET /public/links/{id}
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Link UUID |
Example
curl "https://klinky-api.fly.dev/api/v1/public/links/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: klinky_live_your_api_key_here"Response (200 OK)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "660e8400-e29b-41d4-a716-446655440000",
"organization_id": null,
"slug": "homepage-test",
"name": "Homepage Test",
"is_active": true,
"auto_winner": false,
"auto_winner_threshold": 100,
"routing_type": "ab_test",
"geo_rules": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"variants": [
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"link_id": "550e8400-e29b-41d4-a716-446655440000",
"label": "control",
"destination_url": "https://example.com/control",
"weight": 50,
"is_winner": false,
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": "880e8400-e29b-41d4-a716-446655440000",
"link_id": "550e8400-e29b-41d4-a716-446655440000",
"label": "variant_b",
"destination_url": "https://example.com/variant-b",
"weight": 50,
"is_winner": false,
"created_at": "2024-01-15T10:30:00Z"
}
]
}Delete Link
Soft delete a link. The link will no longer be accessible, but data is preserved for analytics.
Endpoint: DELETE /public/links/{id}
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Link UUID |
Example
curl -X DELETE "https://klinky-api.fly.dev/api/v1/public/links/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: klinky_live_your_api_key_here"Response
204 No Content - Link deleted successfully
Code Examples
Python — Complete Link Management
import requests
API_KEY = "klinky_live_your_api_key_here"
BASE_URL = "https://klinky-api.fly.dev/api/v1"
headers = {"X-API-Key": API_KEY}
# Create a link with auto-winner detection
link_data = {
"name": "Summer Campaign",
"slug": "summer-2024",
"variants": [
{"label": "control", "destination_url": "https://example.com/summer", "weight": 70},
{"label": "promo", "destination_url": "https://example.com/summer-promo", "weight": 30}
],
"auto_winner": True,
"auto_winner_threshold": 200
}
response = requests.post(
f"{BASE_URL}/public/links",
headers=headers,
json=link_data
)
new_link = response.json()
print(f"Created: klinky.io/{new_link['slug']}")
# List all links with pagination
page = 1
while True:
response = requests.get(
f"{BASE_URL}/public/links",
headers=headers,
params={"page": page, "per_page": 20}
)
data = response.json()
for link in data["items"]:
print(f"{link['name']}: {link['total_clicks']} clicks")
if page * 20 >= data["total"]:
break
page += 1
# Get link details
link_id = new_link["id"]
response = requests.get(f"{BASE_URL}/public/links/{link_id}", headers=headers)
details = response.json()
for variant in details["variants"]:
print(f"Variant {variant['label']}: {variant['destination_url']}")
# Delete link
response = requests.delete(f"{BASE_URL}/public/links/{link_id}", headers=headers)
if response.status_code == 204:
print("Link deleted")JavaScript — Link Management
const API_KEY = 'klinky_live_your_api_key_here';
const BASE_URL = 'https://klinky-api.fly.dev/api/v1';
const headers = {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
};
// Create link with auto-winner
const createLink = async () => {
const response = await fetch(`${BASE_URL}/public/links`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'Summer Campaign',
slug: 'summer-2024',
variants: [
{ label: 'control', destination_url: 'https://example.com/summer', weight: 70 },
{ label: 'promo', destination_url: 'https://example.com/summer-promo', weight: 30 }
],
auto_winner: true,
auto_winner_threshold: 200
})
});
return response.json();
};
// List links
const listLinks = async (page = 1, perPage = 20) => {
const response = await fetch(
`${BASE_URL}/public/links?page=${page}&per_page=${perPage}`,
{ headers: { 'X-API-Key': API_KEY } }
);
return response.json();
};
// Get link details
const getLink = async (linkId) => {
const response = await fetch(`${BASE_URL}/public/links/${linkId}`, {
headers: { 'X-API-Key': API_KEY }
});
return response.json();
};
// Delete link
const deleteLink = async (linkId) => {
const response = await fetch(`${BASE_URL}/public/links/${linkId}`, {
method: 'DELETE',
headers: { 'X-API-Key': API_KEY }
});
return response.status === 204;
};cURL — Common Operations
# Create a simple A/B test
curl -X POST https://klinky-api.fly.dev/api/v1/public/links \
-H "X-API-Key: klinky_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Test",
"variants": [
{"label": "a", "destination_url": "https://a.com", "weight": 50},
{"label": "b", "destination_url": "https://b.com", "weight": 50}
]
}'
# Create with idempotency key (safe to retry)
curl -X POST https://klinky-api.fly.dev/api/v1/public/links \
-H "X-API-Key: klinky_live_your_api_key_here" \
-H "Idempotency-Key: unique-request-id-123" \
-H "Content-Type: application/json" \
-d '{...}'
# List first page
curl "https://klinky-api.fly.dev/api/v1/public/links?page=1&per_page=20" \
-H "X-API-Key: klinky_live_your_api_key_here"
# Get specific link
curl "https://klinky-api.fly.dev/api/v1/public/links/YOUR-LINK-ID" \
-H "X-API-Key: klinky_live_your_api_key_here"
# Delete link
curl -X DELETE "https://klinky-api.fly.dev/api/v1/public/links/YOUR-LINK-ID" \
-H "X-API-Key: klinky_live_your_api_key_here"Error Responses
Slug Already Taken (409)
{
"detail": "Slug 'homepage-test' is already taken"
}Resolution: Choose a different slug or omit it to auto-generate one.
Invalid Weights (422)
{
"detail": "Variant weights must sum to 100, got 90"
}Resolution: Adjust weights so they sum to exactly 100.
Invalid Geo Rules (422)
{
"detail": "geo_rules must contain a 'default' variant"
}Or:
{
"detail": "Variant label 'us_page' does not exist. Available variants: control, variant_b"
}Resolution: Ensure all geo rule labels match variant labels and include a default.
Link Not Found (404)
{
"detail": "Link not found"
}See Error Handling for comprehensive error reference.
Best Practices
- Use descriptive names — Make link names clear for future reference
- Plan your variants — Decide on variant labels before creating the link
- Test your URLs — Ensure destination URLs are valid before creating
- Use idempotency keys — For automated link creation to prevent duplicates
- Handle slug conflicts — Have a fallback strategy if your preferred slug is taken
- Monitor via list endpoint — Poll the list endpoint to track link performance
Next Steps
- Analytics API — Retrieve click and conversion data for your links
- Error Handling — Build robust error handling
- Rate Limits — Optimize for high-volume usage
Create your first A/B test link — Start optimizing your campaigns today