API Reference
Use the shorter.sh API to create short links and track clicks programmatically. Authenticate with an API key and start shortening URLs in seconds.
Base URL
https://shorter.sh/api/v1
All requests and responses use JSON. Include Content-Type: application/json in your request headers.
Authentication
API requests are authenticated using API keys. You can create and manage keys from your dashboard.
Sign in with Google or GitHub at /auth
Go to your dashboard and open the API Keys tab
Create a new key. Copy it immediately — it’s only shown once.
Your key looks like this:
sk_a1b2c3d4e5f6... (sk_ prefix + 64 hex characters)
Include it in the Authorization header on every request:
Authorization: Bearer sk_your_api_key_here
Keep your API key secret. Don’t commit it to source control or share it publicly. If compromised, revoke it from your dashboard and create a new one.
Quick Start
Shorten your first URL with a single curl command:
curl -X POST https://shorter.sh/api/v1/shorten \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk_your_api_key_here" \ -d '{"url": "https://example.com/very/long/url"}'
{
"success": true,
"shortCode": "xK9mP2",
"shortUrl": "https://shorter.sh/xK9mP2",
"originalUrl": "https://example.com/very/long/url"
}
Endpoints
Request Body
| Parameter | Type | Description |
|---|---|---|
| url required | string | The URL to shorten. Must start with http:// or https://. Max 2048 characters. |
Response Fields
| Field | Type | Description |
|---|---|---|
| success | boolean | Always true on success |
| shortCode | string | The generated 6-character code |
| shortUrl | string | Full shortened URL ready to share |
| originalUrl | string | The original URL you submitted |
Example
const response = await fetch("https://shorter.sh/api/v1/shorten", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer sk_your_api_key_here", }, body: JSON.stringify({ url: "https://example.com" }), }); const data = await response.json(); console.log(data.shortUrl); // https://shorter.sh/xK9mP2
import requests response = requests.post( "https://shorter.sh/api/v1/shorten", headers={"Authorization": "Bearer sk_your_api_key_here"}, json={"url": "https://example.com"}, ) data = response.json() print(data["shortUrl"]) # https://shorter.sh/xK9mP2
Authentication
Requires API key. Returns URLs owned by the API key holder, sorted by newest first.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number for pagination |
| limit | number | 50 | Results per page (max 100) |
Example
curl "https://shorter.sh/api/v1/urls?page=1&limit=10" \ -H "Authorization: Bearer sk_your_api_key_here"
{
"success": true,
"data": [
{
"id": 42,
"short_code": "xK9mP2",
"original_url": "https://example.com/very/long/url",
"click_count": 127,
"created_at": "2026-01-15T10:30:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 25,
"totalPages": 3
},
"totalClicks": 1580
}
Authentication
Requires API key. You can only delete URLs you own. Deleted links return 410 Gone when visited.
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| shortCode required | string | The 6-character short code |
Example
curl -X DELETE "https://shorter.sh/api/v1/urls/xK9mP2" \ -H "Authorization: Bearer sk_your_api_key_here"
{
"success": true,
"message": "URL deleted"
}
Authentication
Requires API key. You can only access analytics for URLs you created.
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| shortCode required | string | The 6-character short code (e.g. xK9mP2) |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| start | string|number | 30 days ago | Start of date range (ISO date or ms timestamp) |
| end | string|number | Now | End of date range (ISO date or ms timestamp) |
| dimension | string | — | Optional breakdown: country, device_type, browser, os, referrer_domain, language |
| limit | number | 10 | Max breakdown items (max 50) |
Example
curl "https://shorter.sh/api/v1/analytics/xK9mP2?dimension=country" \ -H "Authorization: Bearer sk_your_api_key_here"
{
"success": true,
"summary": {
"totalClicks": 1423,
"uniqueVisitors": 892,
"prevPeriodClicks": 1100,
"prevPeriodUnique": 720,
"topCountry": "US",
"topReferrer": "google.com",
"topDevice": "desktop",
"topBrowser": "Chrome"
},
"timeseries": {
"granularity": "daily",
"data": [
{ "period": 1709164800000, "clicks": 45, "unique_visitors": 30 }
]
},
"breakdown": {
"dimension": "country",
"total": 1423,
"data": [
{ "value": "US", "clicks": 580, "percentage": 40.8 }
]
}
}
The breakdown field is only included when you pass the dimension query parameter. Auto-granularity adjusts based on the date range (hourly for <48h, daily for <90d, weekly for <1y, monthly otherwise).
Authentication
Requires API key. Returns analytics aggregated across all URLs owned by the API key holder.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| start | string|number | 30 days ago | Start of date range (ISO date or ms timestamp) |
| end | string|number | Now | End of date range (ISO date or ms timestamp) |
Example
curl "https://shorter.sh/api/v1/analytics/overview?start=2025-01-01" \ -H "Authorization: Bearer sk_your_api_key_here"
{
"success": true,
"totalClicks": 5420,
"uniqueVisitors": 3100,
"prevPeriodClicks": 4800,
"prevPeriodUnique": 2700,
"timeseries": { "granularity": "daily", "data": [...] },
"topUrls": [
{ "short_code": "xK9mP2", "original_url": "...", "clicks": 580 }
],
"countryBreakdown": [
{ "value": "US", "clicks": 2100, "percentage": 38.7 }
],
"deviceBreakdown": [
{ "value": "desktop", "clicks": 3200, "percentage": 59.0 }
]
}
Rate Limits
API key requests have the following default limits. These can be adjusted by the admin.
| Endpoint | Limit | Window |
|---|---|---|
POST /api/v1/shorten |
30 requests | Per minute |
POST /api/v1/shorten |
500 requests | Per day |
/api/v1/urls* |
60 requests | Per minute |
/api/v1/urls* |
300 requests | Per hour |
/api/v1/analytics/* |
60 requests | Per minute |
/api/v1/analytics/* |
300 requests | Per hour |
When you hit a rate limit, you’ll get a 429 response with a RATE_LIMITED code and a message telling you how many seconds to wait.
{
"success": false,
"message": "Rate limit exceeded. Try again in 42 seconds.",
"code": "RATE_LIMITED"
}
Error Codes
All errors return a JSON object with success: false, a human-readable message, and a machine-readable code.
{
"success": false,
"message": "Description of what went wrong",
"code": "ERROR_CODE"
}
| Code | HTTP | Description |
|---|---|---|
INVALID_API_KEY |
401 | API key is missing, malformed, or not found |
ACCOUNT_SUSPENDED |
403 | Your account has been suspended |
URL_REQUIRED |
400 | No URL was provided in the request body |
INVALID_URL |
400 | URL format is invalid (must be http:// or https://) |
URL_UNSAFE |
400 | URL was flagged as potentially malicious |
RATE_LIMITED |
429 | Too many requests — wait and retry |
AUTH_REQUIRED |
401 | Analytics endpoint requires API key authentication |
FORBIDDEN |
403 | You don’t own this URL |
INVALID_ID |
400 | URL ID is missing or not a valid number |
NOT_FOUND |
404 | URL or resource not found (or not owned by you) |
CREATION_FAILED |
500 | Server error while creating the short URL |
STATS_FAILED |
500 | Server error while fetching stats |