API Authentication
Secure by design. The Klinky API uses proven authentication methods to protect your link data and conversion tracking.
Get API access — Available on Growth and Scale plans
Authentication Methods
Klinky uses two authentication methods depending on the endpoint:
| Method | Use For | Header |
|---|---|---|
| API Key | Public endpoints (links, analytics) | X-API-Key |
| JWT Token | API key management | Authorization: Bearer |
API Key Authentication
Most endpoints require an API key passed in the X-API-Key header:
curl -H "X-API-Key: klinky_live_your_api_key_here" \
https://klinky-api.fly.dev/api/v1/public/linksAPI Key Format
Keys follow this pattern:
klinky_live_{32-character-random-string}Example: klinky_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Security Best Practices
Your API keys grant access to your link data. Protect them accordingly:
- Environment variables — Never hardcode keys in source code
- Separate environments — Use different keys for development and production
- Regular rotation — Rotate keys quarterly or when team members leave
- Immediate revocation — Delete compromised keys instantly
- No client-side exposure — Never expose keys in browser JavaScript or mobile apps
Example: Environment-based configuration
import os
# Good: Key from environment variable
API_KEY = os.environ.get('KLINKY_API_KEY')
# Bad: Key in code
API_KEY = "klinky_live_abc123..."Managing API Keys
API key management requires JWT authentication (your session token from the Klinky web app).
Create API Key
Create a new API key. The full key is returned only once and cannot be retrieved later.
Endpoint: POST /public/keys
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Descriptive name for the key |
Example:
curl -X POST "https://klinky-api.fly.dev/api/v1/public/keys?name=Production%20API%20Key" \
-H "Authorization: Bearer <your-jwt-token>"Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production API Key",
"key": "klinky_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"key_prefix": "klinky_live_a1b2",
"created_at": "2024-01-15T10:30:00Z",
"rate_limit": 1000
}Important: Save the key value immediately. You will not see it again.
List API Keys
List all active API keys for your account. Returns metadata without the actual key values.
Endpoint: GET /public/keys
Example:
curl "https://klinky-api.fly.dev/api/v1/public/keys" \
-H "Authorization: Bearer <your-jwt-token>"Response (200 OK):
{
"keys": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production API Key",
"key_prefix": "klinky_live_a1b2",
"created_at": "2024-01-15T10:30:00Z",
"last_used_at": "2024-01-15T14:22:00Z",
"is_active": true
},
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Development",
"key_prefix": "klinky_live_x9y8",
"created_at": "2024-01-10T09:15:00Z",
"last_used_at": null,
"is_active": true
}
],
"rate_limit_per_hour": 1000
}Revoke API Key
Revoke an API key by ID. This immediately disables the key.
Endpoint: DELETE /public/keys/{key_id}
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
| key_id | UUID | Yes | ID of the key to revoke |
Example:
curl -X DELETE "https://klinky-api.fly.dev/api/v1/public/keys/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer <your-jwt-token>"Response: 204 No Content
Get Rate Limit Status
Check your current rate limit status including remaining requests and reset time.
Endpoint: GET /public/keys/rate-limit
Example:
curl "https://klinky-api.fly.dev/api/v1/public/keys/rate-limit" \
-H "Authorization: Bearer <your-jwt-token>"Response (200 OK):
{
"limit": 1000,
"remaining": 847,
"reset_at": "2024-01-15T15:00:00Z"
}JWT Authentication
JWT tokens are used for API key management and are obtained through the Klinky web application authentication flow. These tokens are short-lived and represent your user session.
To get a JWT token:
- Log in to app.klinky.io
- The token is stored in your browser's localStorage/sessionStorage
- Extract it for API calls (typically for scripting purposes)
For programmatic JWT authentication, use the Supabase auth endpoints directly:
# Sign in to get JWT
curl -X POST "https://your-project.supabase.co/auth/v1/token?grant_type=password" \
-H "apikey: your-anon-key" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "your-password"
}'Error Responses
Invalid API Key (401)
{
"detail": "Invalid API key."
}Resolution: Verify your API key is correct and active.
Missing API Key (401)
{
"detail": "Missing API key. Include X-API-Key header."
}Resolution: Include the X-API-Key header in your request.
Plan Without API Access (403)
{
"detail": "Your plan (free) does not include API access. Upgrade to Growth or Scale."
}Resolution: Upgrade to a Growth or Scale plan to enable API access.
See pricing — Compare plans and features
Code Examples
Python — Complete Key Management
import requests
import os
JWT_TOKEN = os.environ.get('KLINKY_JWT_TOKEN')
BASE_URL = "https://klinky-api.fly.dev/api/v1"
headers = {"Authorization": f"Bearer {JWT_TOKEN}"}
# Create a new API key
response = requests.post(
f"{BASE_URL}/public/keys",
headers=headers,
params={"name": "Production API Key"}
)
new_key = response.json()
print(f"New API key: {new_key['key']}") # Save this securely!
# List all keys
response = requests.get(f"{BASE_URL}/public/keys", headers=headers)
keys = response.json()["keys"]
for key in keys:
print(f"{key['name']}: {key['key_prefix']}... (last used: {key['last_used_at']})")
# Check rate limit status
response = requests.get(f"{BASE_URL}/public/keys/rate-limit", headers=headers)
status = response.json()
print(f"Remaining: {status['remaining']}/{status['limit']}")
# Revoke a key
key_id = "550e8400-e29b-41d4-a716-446655440000"
response = requests.delete(f"{BASE_URL}/public/keys/{key_id}", headers=headers)
if response.status_code == 204:
print("Key revoked successfully")JavaScript — Key Management
const JWT_TOKEN = process.env.KLINKY_JWT_TOKEN;
const BASE_URL = 'https://klinky-api.fly.dev/api/v1';
const headers = {
'Authorization': `Bearer ${JWT_TOKEN}`
};
// Create API key
const createKey = async (name) => {
const response = await fetch(`${BASE_URL}/public/keys?name=${encodeURIComponent(name)}`, {
method: 'POST',
headers
});
const data = await response.json();
console.log('Save this key securely:', data.key);
return data;
};
// List keys
const listKeys = async () => {
const response = await fetch(`${BASE_URL}/public/keys`, { headers });
const data = await response.json();
data.keys.forEach(key => {
console.log(`${key.name}: ${key.key_prefix}...`);
});
};
// Revoke key
const revokeKey = async (keyId) => {
const response = await fetch(`${BASE_URL}/public/keys/${keyId}`, {
method: 'DELETE',
headers
});
return response.status === 204;
};Plan Requirements
API key management requires a paid plan:
| Plan | API Access | Rate Limit | Best For |
|---|---|---|---|
| Free | No | — | Dashboard only |
| Starter | No | — | Dashboard only |
| Growth | Yes | 100/hour | Small teams, basic automation |
| Scale | Yes | 1,000/hour | Large teams, heavy API usage |
Upgrade for API access — Start building with the link shortener API today
Security Checklist
Before deploying to production:
- [ ] API keys stored in environment variables
- [ ] Separate keys for development/staging/production
- [ ] Key rotation process documented
- [ ] Revocation procedure tested
- [ ] Rate limit monitoring in place
- [ ] Error handling for 401/403 responses
Next Steps
- Links API — Create and manage A/B testing links
- Analytics API — Retrieve conversion tracking data
- Error Handling — Handle authentication errors gracefully