#Getting Started
The tny.dev API allows you to programmatically create short links and retrieve analytics data. We offer authenticated API v1 endpoints with rate limiting, custom slugs, and detailed analytics.
Free Tier Features
- ✓ Create short links
- ✓ Basic analytics
- ✓ 50 requests/hour
- ✓ 2 API keys
Developer Tier Features
- ✓ Everything in Free
- ✓ Custom domains
- ✓ Custom slugs (requires custom domain)
- ✓ Bulk operations (100 links/request)
- ✓ Webhooks
- ✓ 500 requests/hour
- ✓ 5 API keys
🚀 New: API v1 with Authentication
Get an API key for enhanced features including custom slugs, rate limiting, and usage tracking.
Get Your API Key →Quick Example
# Create a short link with API key curl -X POST https://www.tny.dev/api/v1/shorten \ -H "Content-Type: application/json" \ -H "X-API-Key: tnyl_your_api_key_here" \ -d '{"url": "https://example.com"}' # Response (201 Created) { "short_url": "https://www.tny.dev/abc123", "slug": "abc123", "long_url": "https://example.com", "created_at": "2024-01-07T10:00:00Z", "qr_download_url": "https://www.tny.dev/api/qr/abc123" }
#Endpoints
POST#/api/v1/shorten
Creates a new shortened URL. Requires API key authentication.Custom slugs and domains requireDevelopertier.
Request Body
Field | Type | Required | Description |
---|---|---|---|
url | string | required | The URL to shorten. Must be a valid HTTP/HTTPS URL. Cannot be localhost or private IP. |
customSlug | string | optionalPaid | Custom slug for the short URL. Must be 3-50 characters, alphanumeric with hyphens and underscores. Requires Developer tier and custom domain. |
domain_id | string | optionalPaid | UUID of your custom domain. Creates short link on your domain instead of tny.dev. |
Examples
curl -X POST https://www.tny.dev/api/v1/shorten \
-H "Content-Type: application/json" \
-H "X-API-Key: tnyl_your_api_key_here" \
-d '{"url": "https://github.com/vercel/next.js"}'
Response
201 Created { "short_url": "https://www.tny.dev/abc123", "slug": "abc123", "long_url": "https://example.com", "created_at": "2024-01-07T10:00:00Z", "qr_download_url": "https://www.tny.dev/api/qr/abc123", // If custom domain was used: "custom_domain": "yourdomain.com", "domain_id": "your-domain-uuid" }
Error Response - Unsafe URL
URLs are checked against Google Web Risk API for malware, phishing, and other threats. If a URL is flagged as unsafe:
400 Bad Request { "error": "This URL has been flagged for containing malware", "code": "UNSAFE_URL", "threat_types": ["MALWARE"] }
Possible threat types: MALWARE
, SOCIAL_ENGINEERING
, UNWANTED_SOFTWARE
, SOCIAL_ENGINEERING_EXTENDED_COVERAGE
GET#/{slug}
Public endpoint that redirects to the original URL and tracks the click event. No authentication required. Also supports HEAD requests for link verification.
Response
301
Redirects to the original URL404
Short link not found
Examples
# Follow redirect
curl -L https://www.tny.dev/abc123
# Check headers only (no redirect)
curl -I https://www.tny.dev/abc123
# See redirect location
curl -I https://www.tny.dev/abc123 | grep Location
Tracked Data
Device & Browser
- • Device type
- • Browser name & version
- • Operating system
- • User agent string
Location & Source
- • Country & city
- • IP address (anonymized)
- • HTTP referrer
- • UTM parameters
POSTDeveloper#/api/v1/shorten/bulk
Create multiple shortened URLs in a single request. Requires Developer tier subscription. Maximum 100 links per request.
Request Body
Field | Type | Required | Description |
---|---|---|---|
links | array | required | Array of link objects to create (max 100) |
links[].url | string | required | The URL to shorten |
links[].customSlug | string | optional | Custom slug for this link. Requires domain_id to be set. |
links[].domain_id | string | optional | Custom domain UUID for this link |
Examples
curl -X POST https://www.tny.dev/api/v1/shorten/bulk \
-H "Content-Type: application/json" \
-H "X-API-Key: tnyl_your_api_key_here" \
-d '{
"links": [
{"url": "https://github.com/vercel/next.js"},
{"url": "https://example.com", "customSlug": "example"}, // Error: custom slug without domain_id
{"url": "https://docs.example.com", "customSlug": "docs", "domain_id": "your-domain-uuid"}
]
}'
Response
201 Created { "success": true, "tier": "developer", "total": 3, "created": 2, "failed": 1, "results": [ { "index": 0, "short_url": "https://www.tny.dev/abc123", "slug": "abc123", "long_url": "https://github.com/vercel/next.js", "created_at": "2024-01-07T10:00:00Z" }, { "index": 1, "short_url": "https://yourdomain.com/docs", "slug": "docs", "long_url": "https://docs.example.com", "created_at": "2024-01-07T10:00:00Z", "custom_domain": "yourdomain.com", "domain_id": "your-domain-uuid" } ], "errors": [ { "index": 1, "url": "https://example.com", "error": "Custom slugs are only available with custom domains." } ] }
GET#/api/v1/links
Lists all links created with your API key. Supports pagination and includes click counts. Use the scope
parameter to fetch all links across all your API keys.
Query Parameters
Parameter | Type | Description |
---|---|---|
page | number | Page number (default: 1) |
limit | number | Items per page (default: 20, max: 100) |
scope | string | Scope of links to return: “key” (default) for current API key only, or “all” for all links across all user’s API keys |
Examples
# Get links created by current API key only (default)
curl https://www.tny.dev/api/v1/links \
-H "X-API-Key: tnyl_your_api_key_here"
# Get all links across all user's API keys
curl "https://www.tny.dev/api/v1/links?scope=all" \
-H "X-API-Key: tnyl_your_api_key_here"
# Get page 2 with 50 items per page
curl "https://www.tny.dev/api/v1/links?page=2&limit=50" \
-H "X-API-Key: tnyl_your_api_key_here"
Response
{ "links": [ { "id": "abc123", "short_url": "https://www.tny.dev/abc123", "long_url": "https://example.com", "created_at": "2024-01-07T12:00:00.000Z", "click_count": 42 } ], "pagination": { "page": 1, "limit": 20, "total": 150, "total_pages": 8 } }
GET#/api/v1/analytics/{slug}
Retrieves comprehensive analytics data for a shortened link. Requires API key authentication. You can access analytics for any link created by any of your API keys.
Examples
# Get analytics data (requires authentication)
curl https://www.tny.dev/api/v1/analytics/abc123 \
-H "X-API-Key: tnyl_your_api_key_here"
Response Structure
{ "link": { "id": "abc123", "long_url": "https://example.com", "created_at": "2024-01-07T10:00:00Z" }, "analytics": { "totalClicks": 1250, "actualClicks": 1000, "previews": 200, "qrScans": 30, "eventTypes": { "click": 1000, "preview": 200, "qr_scan": 30 }, "devices": { "mobile": 600, "desktop": 550, "tablet": 100 }, "countries": { "US": 500, "UK": 200, "CA": 150 }, "referrers": { "twitter.com": 300, "linkedin.com": 200 }, "hourlyClicks": { /* 24 hour breakdown */ }, "utm": { /* UTM parameter breakdown */ }, "recentClicks": [ /* Last 10 clicks */ ] } }
#Custom SlugsDeveloper
Custom slugs allow you to create memorable, branded short links instead of random characters. This feature is available exclusively for Developer tier subscribers and requires the use of custom domains.
Requirements
- ✓ Developer tier subscription ($9/month)
- ✓ At least one verified custom domain
- ✓ Valid API key with authentication
Custom Slug Rules
- • 3-50 characters in length
- • Alphanumeric characters (a-z, A-Z, 0-9)
- • Hyphens (-) and underscores (_) allowed
- • Must be unique within your domain
- • Case-insensitive (treated as lowercase)
Example Usage
{ "url": "https://example.com/products", "customSlug": "summer-sale-2024", "domain_id": "your-domain-uuid" } // Creates: https://yourdomain.com/summer-sale-2024
Important: Custom slugs are only available on your custom domains, not on the default tny.dev domain. You must include a valid domain_id
(UUID) when using custom slugs.
API Implementation
Use the customSlug
parameter in your API requests:
curl -X POST https://www.tny.dev/api/v1/shorten \ -H "Content-Type: application/json" \ -H "X-API-Key: tnyl_your_api_key_here" \ -d '{ "url": "https://example.com/special-offer", "customSlug": "black-friday", "domain_id": "your-domain-uuid" }'
#Custom DomainsDeveloper
Custom domains allow you to create short links using your own domain instead of tny.dev. This feature is available exclusively for Developer tier subscribers.
Setup Process
Custom domains are managed through the web portal. You cannot set up custom domains via the API.
- 1. Go to the Developer Portal
- 2. Navigate to the “Custom Domains” section
- 3. Add your domain and follow the DNS verification steps
- 4. Once verified, you'll receive a domain UUID to use in API calls
Using Custom Domains in API
Once your domain is verified, use the domain_id
parameter (UUID):
{ "url": "https://example.com", "domain_id": "your-domain-uuid" }
Domain Features
- • SSL certificates automatically provisioned
- • Works with both single and bulk endpoints
- • Separate slug namespace per domain
- • Full analytics for custom domain links
- • Webhook support for all events
#WebhooksDeveloper
Webhooks allow you to receive real-time notifications when events occur on your short links. Currently supports link click events. Developer tier subscription required.
Setup Process
Webhooks are managed through the web portal. Like custom domains, webhooks cannot be created or managed via the API.
- 1. Go to the Account Portal
- 2. Navigate to the “Webhooks” tab
- 3. Add your webhook endpoint URL
- 4. Copy the generated API key to verify incoming webhooks
- 5. Test your webhook with the built-in testing tool
Webhook Features
- • Real-time link click notifications
- • Simple API key authentication
- • Automatic retries on failure
- • Delivery logs and monitoring
- • Maximum 5 webhooks per account
Events Supported
- •
link.clicked
- Triggered when a short link is clicked - • More events coming soon...
Note: Only links created via the API will trigger webhooks. Links created through the web interface do not have an associated user and therefore cannot trigger webhook notifications.
Webhook Integration Examples
Here's how to receive and verify webhooks in your application:
// Express.js webhook receiver
app.post('/webhook', (req, res) => {
const apiKey = req.headers['x-api-key'];
// Verify the webhook is from tny.dev
if (apiKey !== process.env.WEBHOOK_API_KEY) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Process the webhook payload
const { event, data } = req.body;
console.log('Received event:', event);
console.log('Link clicked:', data.link_id);
// Handle different event types
switch(event) {
case 'link.clicked':
// Process link click
break;
}
res.json({ received: true });
});
Webhook Payload
When a link is clicked, we send a POST request to your webhook URL:
{ "event": "link.clicked", "timestamp": "2024-01-07T10:00:00Z", "data": { "link_id": "abc123", "short_url": "https://www.tny.dev/abc123", "long_url": "https://example.com", "click_data": { "ip": "192.0.2.1", "user_agent": "Mozilla/5.0...", "referer": "https://twitter.com", "country": "US", "city": "New York", "device_type": "mobile", "browser": "Chrome", "os": "iOS" } } }
Webhook Authentication
Each webhook request includes your API key in the X-API-Key
header for authentication:
Headers: X-API-Key: whk_your_webhook_api_key X-Webhook-Event: link.clicked X-Webhook-Timestamp: 2024-01-07T10:00:00Z Content-Type: application/json User-Agent: tny.dev-webhooks/2.0
Simple verification example:
// Express.js app.post('/webhook', (req, res) => { if (req.headers['x-api-key'] !== process.env.WEBHOOK_API_KEY) { return res.status(401).json({ error: 'Unauthorized' }); } // Process webhook console.log('Event:', req.body.event); res.json({ received: true }); });
#Authentication
All API v1 endpoints require authentication via API key. Get your API key from the Developer Portal.
Using Your API Key
Include your API key in the X-API-Key
header for all authenticated requests:
curl https://www.tny.dev/api/v1/shorten \ -H "X-API-Key: tnyl_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}'
Key Format
API keys follow this format:
tnyl_32_character_random_string
Key Limits
- • Maximum 2 active keys per account
- • 50 requests/hour per key (default)
- • Keys can be deactivated anytime
Security: Keep your API keys secure. Never commit them to version control or expose them in client-side code.
#Error Handling
Status Code | Description | Example |
---|---|---|
400 | Bad Request | Invalid URL, unsafe content, or invalid request body |
401 | Unauthorized | Missing or invalid API key |
402 | Payment Required | Feature requires Developer subscription (custom slugs, domains, webhooks, bulk) |
403 | Forbidden | No access to this resource (e.g., another user's analytics) |
404 | Not Found | Link, webhook, or resource not found |
409 | Conflict | Custom slug already exists for this domain |
422 | Unprocessable Entity | Invalid custom slug format |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Failed to create short link |
Note: All errors return a JSON object with an error
field containing a human-readable message.
Common Error Messages
- • “Invalid URL. Please provide a valid HTTP/HTTPS URL.”
- • “This URL has been flagged as unsafe”
- • “Custom slugs require Developer plan”
- • “Domain not found, not verified, or not active.”
- • “Webhook limit reached. Maximum 5 webhooks allowed.”
Feature-Specific Errors
- Custom Slugs: 402 for tier restriction, 422 for format errors
- Custom Domains: 402 for tier restriction, 400 for invalid domain
- Bulk Operations: 402 if not on Developer tier
- Webhooks: 402 for tier restriction, 400 for limit exceeded
- Analytics: 403 for accessing other users' links
#Rate Limiting
Free Tier
50
requests per hour per key
Max 2 keys per account
Developer Tier
Upgrade →500
requests per hour per key
$9/month
- ✓ Everything in Free
- ✓ Custom domains
- ✓ Bulk operations (100 links/request)
- ✓ Webhooks
- ✓ 500 requests/hour
- ✓ 5 API keys
Rate Limit Headers
All API responses include rate limit information:
- •
X-RateLimit-Limit
- Your hourly request limit - •
X-RateLimit-Remaining
- Requests remaining this hour - •
X-RateLimit-Reset
- Time when limit resets (Unix timestamp)