API Documentation

Programmatic access to Zi.Ink URL shortener

Authentication

All API requests require authentication.

API Key (Recommended for programmatic access)

Include your API key in the Authorization header:

Authorization: Bearer zi_ink_your_api_key_here

Get your API key from the API Keys page. API access requires a paid plan.

API Key Limits:
  • Pro: 1 API key
  • Enterprise: 10 API keys
  • Free: No API access

Base URL

https://zi.ink

🌐 Using Custom Domains

To use your custom domain with the API, you need to get your domain_id first:

Method 1: Get from /api/domains

curl https://zi.ink/api/domains \ -H "Authorization: Bearer zi_ink_your_key"
{ "domains": [ { "id": "72fa7279-65ca-4fa6-9407-63f1d711fb68", "domain": "yourdomain.com", "status": "active", "subscription_status": "active" } ]
}

Method 2: Get from /api/links

The /api/links response includes a customDomains array with your active domains.

⚠️ Important: Slugs and Domain Identification

The same slug can exist on multiple domains (e.g., zi.ink/abc123 and yourdomain.com/abc123 are different links). When using GET, DELETE, or PATCH on a slug, use the domain_id query parameter to specify which domain's link you're targeting. If omitted, the API defaults to zi.ink domain links.

💡 Tip: Copy the id value and use it as domain_id in your API requests.

POST/api/shorten

Create a new shortened link.

Request Body

{ "long_url": "https://example.com/very-long-url", "custom_slug": "optional-custom", "domain_id": "optional-domain-id"
}
  • long_url - The URL to shorten (required)
  • custom_slug - Custom short code (optional, 3-50 chars, alphanumeric + hyphen/underscore)
  • domain_id - Your custom domain ID (optional, requires active custom domain subscription)

Response (201)

{ "success": true, "slug": "abc1234", "short_url": "https://zi.ink/abc1234", "domain": null
}

Note: When using a custom domain, short_url will use your custom domain (e.g., https://yourdomain.com/abc1234).

Example - Default Domain

curl -X POST https://zi.ink/api/shorten -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{"long_url":"https://example.com/page"}'

Example - With Custom Domain

# First, get your domain_id from /api/domains
# Then use it in the request:
curl -X POST https://zi.ink/api/shorten -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{"long_url":"https://example.com/page","domain_id":"72fa7279-65ca-4fa6-9407-63f1d711fb68"}' # Response will use your custom domain:
# {"success":true,"short_url":"https://yourdomain.com/abc123","slug":"abc123","domain":"yourdomain.com"}
GET/api/links?page=1&limit=20

Get paginated links for authenticated user. Use /api/analytics/:slug for click statistics.

Query Parameters

page(optional) - Page number (default: 1)
limit(optional) - Items per page (default: 20, max: 100)

Response (200)

{ "links": [ { "id": 1, "slug": "abc1234", "long_url": "https://example.com", "short_url": "https://zi.ink/abc1234", "user_id": 1, "domain_id": null, "domain_name": "zi.ink", "domain_expired": false, "password_protected": false, "created_at": "2026-01-27T10:00:00.000Z", "expires_at": null } ], "total": 150, "page": 1, "limit": 20, "totalPages": 8, "customDomains": [], "subscriptionExpired": null
}

Note: Click statistics are not included in this endpoint for performance. Use /api/analytics/:slug to get detailed analytics for a specific link.

Example

curl "https://zi.ink/api/links?page=1&limit=20" -H "Authorization: Bearer zi_ink_your_key"
DELETE/api/links/:slug?domain_id=xxx

Delete a link by slug. If you have the same slug on multiple domains, use domain_id query parameter to specify which one.

Query Parameters

domain_id(optional) - Custom domain ID. Omit for zi.ink links.

Response (200)

{ "success": true, "message": "Link deleted successfully"
}

Example - Delete from zi.ink

curl -X DELETE https://zi.ink/api/links/abc1234 \ -H "Authorization: Bearer zi_ink_your_key"

Example - Delete from Custom Domain

curl -X DELETE "https://zi.ink/api/links/abc123?domain_id=72fa7279-65ca-4fa6-9407-63f1d711fb68" \ -H "Authorization: Bearer zi_ink_your_key"
GET/api/links/:slug?domain_id=xxx

Get details for a specific link including click count. If you have the same slug on multiple domains, use domain_id query parameter.

Query Parameters

domain_id(optional) - Custom domain ID. Omit for zi.ink links.

Response (200)

{ "link": { "id": 746, "slug": "abc1234", "long_url": "https://example.com", "user_id": 1, "created_at": "2026-01-27T10:00:00.000Z", "expires_at": null, "password_protected": false, "short_url": "https://zi.ink/abc1234", "clicks": 42 }
}

Example - Get zi.ink Link

curl https://zi.ink/api/links/abc1234 \ -H "Authorization: Bearer zi_ink_your_key"

Example - Get Custom Domain Link

curl "https://zi.ink/api/links/abc123?domain_id=72fa7279-65ca-4fa6-9407-63f1d711fb68" \ -H "Authorization: Bearer zi_ink_your_key"
PATCH/api/links/:slug?domain_id=xxxEnterprise Only

Update link settings including destination URL, expiration, and password protection. Enterprise plan required. If you have the same slug on multiple domains, use domain_id query parameter.

Query Parameters

domain_id(optional) - Custom domain ID. Omit for zi.ink links.

Request Body

{ "long_url": "https://new-destination.com", "expires_at": "2026-12-31T23:59:59Z", "password_enabled": true, "password": "secretpass123"
}
  • long_url - New destination URL (optional)
  • expires_at - Expiration date in ISO 8601 format, or null to remove (optional)
  • password_enabled - Enable/disable password protection (optional)
  • password - Password for protection, required when enabling (optional)

Response (200)

{ "success": true, "message": "Link updated successfully", "link": { "slug": "abc1234", "long_url": "https://new-destination.com", "expires_at": "2026-12-31T23:59:59.000Z", "password_protected": true }
}

Example

curl -X PATCH https://zi.ink/api/links/abc1234 \ -H "Authorization: Bearer zi_ink_your_key" \ -H "Content-Type: application/json" \ -d '{"expires_at":"2026-12-31T23:59:59Z","password_enabled":true,"password":"secret"}'
POST/api/bulk-shortenPro & Enterprise Only

Bulk shorten up to 100 URLs in a single request. Requires Pro or Enterprise plan.

Request Body

{ "urls": [ "https://example.com/page1", "https://example.com/page2", "https://example.com/page3" ], "domain_id": "optional-domain-id"
}
  • urls - Array of URLs to shorten (required, max 100)
  • domain_id - Your custom domain ID (optional, applies to all URLs)

Response (201) - Completed Immediately

For small batches (≤10 URLs), processing is synchronous and results are returned immediately:

{ "success": true, "jobId": "bulk-1770004251559-ous1bln", "status": "completed", "total": 3, "processed": 3, "successful": 3, "failed": 0, "results": [ { "original_url": "https://example.com/page1", "short_url": "https://zi.ink/abc123", "slug": "abc123", "success": true } ], "message": "Bulk processing completed in sync mode"
}

Response (201) - Processing Async

For larger batches (>10 URLs), processing is asynchronous:

{ "success": true, "jobId": "bulk-1769661366024-abc123"
}

Use the jobId with /api/bulk-status/:jobId to check progress.

Example - Default Domain

curl -X POST https://zi.ink/api/bulk-shorten -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{"urls":["https://example.com/1","https://example.com/2"]}'

Example - With Custom Domain

# First, get your domain_id from /api/domains
# Then use it in the request to apply custom domain to all URLs:
curl -X POST https://zi.ink/api/bulk-shorten -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{"urls":["https://example.com/1","https://example.com/2"],"domain_id":"72fa7279-65ca-4fa6-9407-63f1d711fb68"}' # Response will use your custom domain for all URLs:
# {"success":true,"status":"completed","results":[{"short_url":"https://yourdomain.com/abc123",...}]}
GET/api/bulk-status/:jobIdPro & Enterprise Only

Check the status of a bulk shorten job.

Response (200) - Pending

{ "jobId": "bulk-1769661366024-abc123", "status": "pending", "progress": 50, "total": 100, "processed": 50
}

Response (200) - Completed

{ "jobId": "bulk-1769661366024-abc123", "status": "completed", "total": 3, "successful": 3, "failed": 0, "results": [ { "original_url": "https://example.com/page1", "success": true, "short_url": "https://zi.ink/abc123", "slug": "abc123" } ]
}

Example

curl https://zi.ink/api/bulk-status/bulk-1769661366024-abc123 -H "Authorization: Bearer zi_ink_your_key"
POST/api/links/bulk-deletePro & Enterprise Only

Bulk delete multiple links. Supports both zi.ink and custom domain links. Requires Pro or Enterprise plan.

Request Body (Option 1 - Simple)

{ "slugs": ["abc1234", "xyz5678"], "domain_id": "optional-domain-id"
}

Note: If domain_id is omitted, all slugs default to zi.ink domain. If provided, all slugs will be deleted from that custom domain.

Request Body (Option 2 - Per-Link Domain)

{ "links": [ {"slug": "abc123", "domain_id": null}, {"slug": "xyz789", "domain_id": "your-domain-id"} ]
}

Note: Use this format when deleting links from multiple domains in one request. Set domain_id: null for zi.ink links.

Response (200)

{ "success": true, "deleted": 2, "failed": 0, "failedSlugs": []
}

Example - Delete from zi.ink

curl -X POST https://zi.ink/api/links/bulk-delete -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{"slugs":["abc1234","xyz5678"]}'

Example - Delete from Custom Domain

# Delete specific slugs from custom domain
curl -X POST https://zi.ink/api/links/bulk-delete -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{"slugs":["abc123","xyz789"],"domain_id":"72fa7279-65ca-4fa6-9407-63f1d711fb68"}'

Example - Delete from Mixed Domains

# Delete links from both zi.ink and custom domains
curl -X POST https://zi.ink/api/links/bulk-delete -H "Authorization: Bearer zi_ink_your_key" -H "Content-Type: application/json" -d '{ "links": [ {"slug": "abc123", "domain_id": null}, {"slug": "custom1", "domain_id": "72fa7279-65ca-4fa6-9407-63f1d711fb68"} ]
}'
GET/api/user

Get current authenticated user information.

Response (200)

{ "user": { "email": "user@example.com", "name": "John Doe", "picture": "https://example.com/avatar.jpg", "plan_tier": "enterprise", "created_at": "2026-01-01T00:00:00.000Z" }
}

Example

curl https://zi.ink/api/user -H "Authorization: Bearer zi_ink_your_key"
GET/api/analytics/:slug

Get analytics data for a specific link.

Query Parameters

  • days - Number of days to fetch (default: 7, max: 365)

Response (200)

{ "slug": "abc1234", "long_url": "https://example.com", "created_at": "2026-01-27T10:00:00.000Z", "total_clicks": 533, "by_country": [...], "by_referrer": [...], "by_browser": [...], "by_device": [...]
}

Example

curl https://zi.ink/api/analytics/abc1234?days=30 -H "Authorization: Bearer zi_ink_your_key"

Rate Limits

Plan-Based Limits (per hour, shared)

  • Free: 100 links/hour
  • Pro: 2,000 links/hour (20x free)
  • Enterprise: 20,000 links/hour (200x free)

📝 Note: Rate limits are shared between /api/shorten and /api/bulk-shorten endpoints. For example, if you create 50 links via single API and 50 via bulk, you've used 100 of your hourly limit.

Additional Limits

  • Global IP Limit: 10,000 requests per hour (all requests including browsing)
  • /api/shorten (Guest): 10 requests per minute (unauthenticated users)
  • Redirects: 100 requests per minute per IP

💡 Tip: Upgrade to Pro or Enterprise for higher rate limits and bulk processing capabilities.

Rate limit info is included in response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Plan

Error Codes

400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
404Not Found - Resource doesn't exist
409Conflict - Slug already exists
429Too Many Requests - Rate limit exceeded
500Internal Server Error