Authentication
All API requests require authentication using a Bearer token. Generate your API token from your account settings or via the API.
Authorization: Bearer your_api_token_here
Paid Subscription Required
API access requires a paid subscription (Starter, Pro, Business, or Enterprise). Free accounts cannot use the Developer API.
Token Permissions
When creating API tokens, you can specify granular abilities to limit what the token can access:
| Ability | Access |
|---|---|
* |
Full access to all endpoints |
links:read |
View and list links |
links:write |
Create, update, and delete links |
analytics:read
|
View click analytics and metrics |
qr:read |
View and list QR codes |
qr:write |
Create, update, and delete QR codes |
user:read |
View profile, tokens, and teams |
user:write |
Update profile and manage tokens |
Token Security
Keep your API tokens secure. Never share them in public repositories or client-side code. Use limited abilities for integrations that don't need full access.
Rate Limiting & Plan Limits
Per-minute rate limits
API requests are rate-limited based on your subscription plan. Exceeding this returns a 429.
| Plan | Requests / minute |
|---|---|
| Starter | 60 |
| Pro / Business | 120 |
| Enterprise | 500 |
Monthly plan limits
In addition to per-minute limits, each plan has monthly caps on resource creation. Counters reset at the start of each calendar month. Exceeding a monthly cap returns a 429 with a message indicating the limit.
| Plan | Links / mo | QR codes / mo | API calls / mo | Analytics |
|---|---|---|---|---|
| Starter | 100 | 25 | 5,000 | 90 days |
| Pro | 1,000 | 250 | 50,000 | 365 days |
| Enterprise | Unlimited | Unlimited | Unlimited | Forever |
Use GET /v1/user/limits to check your current usage and remaining allowance at any time.
If you purchase extra link or QR credits from the /credits page, those top‑ups are automatically used for API creations after your plan's monthly allowance is exhausted (team-shared).
Response Format
All responses are returned in JSON format with a consistent structure.
Success Response
{
"status": true,
"message": "Success",
"data": { ... }
}
Paginated Response
{
"status": true,
"message": "Success",
"data": [ ... ],
"meta": {
"pagination": {
"current_page": 1,
"per_page": 25,
"total": 100,
"total_pages": 4,
"has_more": true
}
}
}
Error Response
{
"status": false,
"message": "An error occurred",
"data": [],
"errors": {
"long_url": ["The long url field is required."]
}
}
Error Codes
| HTTP Status | Description |
|---|---|
| 401 | Unauthenticated - Invalid or missing token |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource not found |
| 422 | Validation Error - Request validation failed |
| 429 | Rate Limit Exceeded — too many requests per minute, or monthly plan limit reached (links / QR codes) |
| 500 | Internal Error - Server error |
Links
/v1/shorten
Quick-shorten a URL with minimal parameters.
Request
{
"long_url": "https://example.com/very/long/url"
}
Response (201 Created)
{
"status": true,
"message": "Link created successfully.",
"data": {
"id": 42,
"slug": "aB3x7kL",
"link": "https://lynklens.com/aB3x7kL",
"long_url": "https://example.com/very/long/url",
"title": "Example",
"status": "active",
"source": "api",
"has_qr_attached": false,
"created_at": "2025-12-27T10:00:00+00:00"
}
}
/v1/links
Create a link with full customization options.
Request Body
| Field | Type | Description |
|---|---|---|
long_url |
string | Required. The destination URL |
title |
string | Custom title for the link |
description |
string | Description (max 1000 chars) |
tags |
array | Array of tags for categorization |
expires_at |
datetime | Expiration date (ISO 8601) |
team_id |
integer | Assign to a team |
Response same as POST /v1/shorten
/v1/links
List all links for the authenticated user.
Query Parameters
page |
Page number (default: 1) |
per_page |
Items per page, max 100 (default: 25) |
status |
Filter: active, inactive, archived |
source |
Filter: web, api |
query |
Search in title, URL, or slug |
tags |
Filter by tags (comma-separated) |
Response (200 OK)
{
"status": true,
"message": "Success",
"data": [
{
"id": 42,
"slug": "aB3x7kL",
"link": "https://lynklens.com/aB3x7kL",
"long_url": "https://example.com/page",
"title": "Example",
"status": "active",
"source": "api",
"has_qr_attached": true
}
],
"meta": {
"pagination": {
"current_page": 1,
"per_page": 25,
"total": 150,
"total_pages": 6,
"has_more": true
}
}
}
/v1/links/{slug}
Retrieve a specific link by its slug. Includes total click count.
Response (200 OK)
{
"status": true,
"data": {
"slug": "aB3x7kL",
"link": "https://lynklens.com/aB3x7kL",
"long_url": "https://example.com/page",
"title": "Example",
"tags": ["marketing", "campaign"],
"status": "active",
"source": "api",
"total_clicks": 1543
}
}
/v1/links/{slug}
Update a link's properties. All fields are optional.
Request
{
"long_url": "https://example.com/updated",
"title": "Updated Title",
"tags": ["updated"],
"status": "inactive"
}
Response (200 OK)
{
"status": true,
"message": "Link updated successfully.",
"data": { /* updated link object */ }
}
/v1/links/{slug}
Delete a link and its associated files (favicon, QR code).
Response (200 OK)
{
"status": true,
"message": "Link deleted successfully.",
"data": []
}
/v1/expand
Expand a short link to get the original long URL.
Request
{
"slug": "aB3x7kL"
}
Response (200 OK)
{
"status": true,
"data": {
"slug": "aB3x7kL",
"link": "https://lynklens.com/aB3x7kL",
"long_url": "https://example.com/page",
"created_at": "2025-12-27T10:00:00+00:00"
}
}
Analytics
All analytics endpoints accept these query parameters:
unit (minute/hour/day/week/month), units (number of periods 1-365),
size (results limit).
/v1/links/{slug}/clicks
Get click counts over time, grouped by time period.
Response (200 OK)
{
"status": true,
"data": {
"clicks": [
{ "date": "2025-12-27", "clicks": 45 },
{ "date": "2025-12-26", "clicks": 72 },
{ "date": "2025-12-25", "clicks": 31 }
],
"unit": "day",
"units": 7
}
}
/v1/links/{slug}/clicks/summary
Get total click count for a specified time period.
Response (200 OK)
{
"status": true,
"data": {
"total_clicks": 1543,
"unit": "month",
"units": 1
}
}
/v1/links/{slug}/countries
Get clicks breakdown by country.
Response (200 OK)
{
"status": true,
"data": {
"metrics": [
{ "country": "United States", "clicks": 523 },
{ "country": "United Kingdom", "clicks": 234 },
{ "country": "Germany", "clicks": 156 }
]
}
}
/v1/links/{slug}/devices
Get clicks breakdown by device type (desktop, mobile, tablet).
Response (200 OK)
{
"status": true,
"data": {
"metrics": [
{ "device": "mobile", "clicks": 823 },
{ "device": "desktop", "clicks": 612 },
{ "device": "tablet", "clicks": 108 }
]
}
}
Other Analytics Endpoints
/v1/links/{slug}/cities - Breakdown by city
/v1/links/{slug}/referrers - Breakdown by referrer source
/v1/links/{slug}/browsers - Breakdown by browser
/v1/links/{slug}/operating-systems - Breakdown by OS
All return the same response format as /countries and /devices.
QR Codes
/v1/qr-codes
Create a QR code for an existing link.
Request Body
link_slug |
Required. The link's slug |
color |
QR color hex (default: #000000) |
background_color |
Background hex (default: #ffffff) |
size |
Size in pixels, 100-1000 (default: 300) |
format |
png or svg (default: png) |
Response (201 Created)
{
"status": true,
"message": "QR code created successfully.",
"data": {
"link_slug": "aB3x7kL",
"qr_image_url": "https://lynklens.com/storage/qr/aB3x7kL.png",
"color": "#000000",
"size": 300
}
}
/v1/qr-codes/{slug}
Get QR code details including total scan count.
Response (200 OK)
{
"status": true,
"data": {
"link_slug": "aB3x7kL",
"link_url": "https://lynklens.com/aB3x7kL",
"long_url": "https://example.com/page",
"title": "Example",
"qr_image_url": "https://lynklens.com/storage/qr/aB3x7kL.png",
"total_scans": 234
}
}
Other QR Code Endpoints
/v1/qr-codes - List all QR codes (paginated)
/v1/qr-codes/{slug}/image - Get QR image file or base64
/v1/qr-codes/{slug} - Regenerate with new settings
/v1/qr-codes/{slug} - Remove QR code from link
/v1/qr-codes/{slug}/scans - Scan counts over time
/v1/qr-codes/{slug}/scans/summary - Total scan count
User
/v1/user
Get authenticated user information including subscription and stats.
Response (200 OK)
{
"status": true,
"data": {
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"is_verified": true,
"subscription": { "plan": "Pro", "status": "active" },
"stats": { "total_links": 150, "total_qr_codes": 45 }
}
}
/v1/user/tokens
Create a new API token. Save the token immediately - it won't be shown again!
Response (201 Created)
{
"status": true,
"data": {
"token": "1|abc123def456...",
"id": 5,
"name": "My Integration"
}
}
/v1/user/limits
Returns your plan's limits, your current usage this calendar month, and how many creations you have remaining. Use this to display usage dashboards or warn users before they hit a cap.
Limits and usage are team-scoped. By default, this endpoint uses your current team.
Optionally pass team_id to fetch limits/usage for a specific team you belong to.
Response (200 OK)
{
"status": true,
"data": {
"rate_limit": {
"limit": 60,
"window": "minute"
},
"plan_limits": {
"links_per_month": 100,
"qr_codes_per_month": 25,
"api_calls_per_month": 5000,
"custom_slugs": true,
"analytics_retention_days": 90
},
"usage": {
"links": {
"used": 38,
"limit": 100,
"remaining": 62,
"unlimited": false
},
"qr_codes": {
"used": 7,
"limit": 25,
"remaining": 18,
"unlimited": false
},
"resets_at": "2026-03-31T23:59:59+00:00"
}
}
}
Enterprise users will see -1 for limits,
null for remaining, and "unlimited": true.
Admins may set custom overrides that differ from plan defaults.
Other User Endpoints
/v1/user - Update user profile
/v1/user/tokens - List your API tokens
/v1/user/tokens/{id} - Revoke a token
Groups (Teams)
/v1/groups
List all teams the authenticated user has access to.
Response (200 OK)
{
"status": true,
"data": [
{
"id": 1,
"name": "Marketing Team",
"personal_team": false,
"role": "admin",
"is_owner": true
},
{
"id": 2,
"name": "Personal",
"personal_team": true,
"role": "owner",
"is_owner": true
}
]
}
Coming Soon
Additional group endpoints for accessing team-specific links, QR codes, and analytics will be available in a future API version.
Need Help?
Have questions about the API? We're here to help.
Contact Support