Checking status...
tny.dev Logo

API Docs

Reference for devs and agentsllms.txt

#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

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

  • 301Redirects to the original URL
  • 404Short 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

FieldTypeRequiredDescription
linksarrayrequiredArray of link objects to create (max 100)
links[].urlstringrequiredThe URL to shorten
links[].customSlugstringoptionalCustom slug for this link. Requires domain_id to be set.
links[].domain_idstringoptionalCustom 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/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
Upgrade to Developer Tier →

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. 1. Go to the Developer Portal
  2. 2. Navigate to the “Custom Domains” section
  3. 3. Add your domain and follow the DNS verification steps
  4. 4. Once verified, you'll receive a domain UUID to use in API calls
Manage Custom Domains →

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. 1. Go to the Account Portal
  2. 2. Navigate to the “Webhooks” tab
  3. 3. Add your webhook endpoint URL
  4. 4. Copy the generated API key to verify incoming webhooks
  5. 5. Test your webhook with the built-in testing tool
Manage Webhooks →

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 CodeDescriptionExample
400Bad RequestInvalid URL, unsafe content, or invalid request body
401UnauthorizedMissing or invalid API key
402Payment RequiredFeature requires Developer subscription (custom slugs, domains, webhooks, bulk)
403ForbiddenNo access to this resource (e.g., another user's analytics)
404Not FoundLink, webhook, or resource not found
409ConflictCustom slug already exists for this domain
422Unprocessable EntityInvalid custom slug format
429Too Many RequestsRate limit exceeded
500Internal Server ErrorFailed 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)