Developers
ShrinkRL API
Shorten URLs, set custom slugs, read click analytics, and create links in bulk — over a simple JSON REST API. No API key. No auth. Just call it.
Overview
Every endpoint is open — no headers or keys required. A bulk request counts as a single call against the rate limit, no matter how many URLs it carries. High-volume partners can request a higher per-IP limit.
og_image: null and metadata: {}; fetch the link again with GET /api/v1/urls/:slug a moment later to get them.Create a short link
/api/v1/urlsBody — wrap fields in a url object. slug (custom, 3–20 chars of a–z, A–Z, 0–9, -_) and expires_at (ISO-8601) are optional.
POST /api/v1/urls
Content-Type: application/json
{ "url": { "original_url": "https://example.com/a/very/long/page" } }{
"short_url": "https://www.shrinkrl.com/kDgxDc",
"original_url": "https://example.com/a/very/long/page",
"og_image": null,
"slug": "kDgxDc",
"metadata": {}
}Custom-slug collisions are idempotent
- Same
original_url→ 200 with the existing link (safe to retry — no duplicate created). - Different
original_url→ 409 with the existing link in the body pluserrors.
Read a link & its stats
/api/v1/urls/:slugReturns the link plus metadata and click count. Does not count as a click — use this for dashboards and previews.
{
"short_url": "https://www.shrinkrl.com/kDgxDc",
"original_url": "https://example.com/a/very/long/page",
"og_image": "https://.../preview.png",
"click_count": 42,
"created_at": "2026-05-29T08:29:10.409Z",
"slug": "kDgxDc",
"metadata": {
"title": "Example page title",
"description": "A short description of the destination.",
"image": "https://.../preview.png",
"site_name": "Example.com"
}
}/api/v1/urls/:slug/dataSame payload, but counts as a click. Use it when a user actually opens the link.
/api/v1/urls/:slugDeletes the link. Returns 204 No Content.
Bulk operations
/api/v1/urls/bulk_create≤ 500 URLs are created immediately and returned in the response. > 500 are processed in the background and you get a bulk_id to poll.
{ "urls": [
{ "original_url": "https://a.com" },
{ "original_url": "https://b.com", "slug": "launch-b" }
] }{
"status": "completed",
"total": 2, "successful": 2, "failed": 0,
"urls": [ { "short_url": "...", "slug": "...", ... } ],
"errors": []
}{
"bulk_id": "uuid",
"status": "processing",
"check_status_url": "/api/v1/urls/bulk/uuid/status"
}/api/v1/urls/bulk/:bulk_id/statusPoll until status is completed, then read urls / errors.
/api/v1/urls/bulk_dataRead up to 1,000 links at once.
{ "slugs": ["abc123", "def456"] }Redirects & health
/r/:slugServer-side 302 redirect to the original URL (the click is tracked automatically). Sharing the short link https://www.shrinkrl.com/:slug works too.
/healthReturns { "status": "OK" } when the API is healthy.
Code examples
curl -s -X POST https://api.shrinkrl.com/api/v1/urls \
-H "Content-Type: application/json" \
-d '{"url":{"original_url":"https://example.com"}}'const API = "https://api.shrinkrl.com";
const res = await fetch(`${API}/api/v1/urls`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url: { original_url: "https://example.com" } }),
});
const { short_url } = await res.json();
// metadata is async — fetch it a moment later (non-tracking endpoint):
const link = await (await fetch(`${API}/api/v1/urls/${slug}`)).json();import requests
API = "https://api.shrinkrl.com"
r = requests.post(f"{API}/api/v1/urls",
json={"url": {"original_url": "https://example.com"}})
r.raise_for_status()
short_url = r.json()["short_url"]