URL Shortener API
A tiny Sinatra service. Shorten a URL, get a redirect, inspect stats.
Base URL
https://url.keychain.pl
Endpoints
Shorten a URL. Body is JSON with a single url field. Returns the new record and a fully qualified short_url you can hand to a browser.
curl -X POST https://url.keychain.pl/api/shorten \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/some/long/path"}'
Response 201
{
"id": "aZ3kQ9m",
"target_url": "https://example.com/some/long/path",
"created_at": "2026-07-04T18:22:31Z",
"click_count": 0,
"short_url": "https://url.keychain.pl/aZ3kQ9m"
}
Try it
The form posts JSON via the _demo hint; your browser will show the raw response.
| Status | Cause |
|---|---|
| 400 | Body is not valid JSON. |
| 422 | url missing or not an http(s) URL. |
Resolve a short id to its target. Increments click_count atomically and returns a 302 Found to the original URL.
curl -i https://url.keychain.pl/aZ3kQ9m
# HTTP/1.1 302 Found
# Location: https://example.com/some/long/path
Errors
| Status | Cause |
|---|---|
| 404 | No record exists for that id. |
Read metadata for a single short URL. Useful for building a stats dashboard without following the redirect.
Requestcurl https://url.keychain.pl/api/urls/aZ3kQ9m
Response 200
{
"id": "aZ3kQ9m",
"target_url": "https://example.com/some/long/path",
"created_at": "2026-07-04T18:22:31Z",
"click_count": 4
}
List every short URL this server knows about, newest first.
Requestcurl https://url.keychain.pl/api/urls
Response 200
{
"count": 2,
"urls": [
{
"id": "aZ3kQ9m",
"target_url": "https://example.com/some/long/path",
"created_at": "2026-07-04T18:22:31Z",
"click_count": 4
},
{
"id": "b7H2nXp",
"target_url": "https://sinatrarb.com/",
"created_at": "2026-07-04T18:25:02Z",
"click_count": 0
}
]
}
How ids are generated
Each id is 7 random characters from a 62-symbol alphabet (a-z A-Z 0-9), giving ~3.5 trillion possibilities. Collisions are extremely rare but checked and retried up to 5 times at insert time.
Storage
Google Cloud Firestore in Native mode, on the Spark free tier (1 GiB storage, 50K reads / 20K writes per day, no billing account required). Configure with FIRESTORE_PROJECT_ID (required) and FIRESTORE_COLLECTION (optional, defaults to urls). Locally, GOOGLE_APPLICATION_CREDENTIALS points at a service-account JSON key; on Cloud Run the runtime service account is used automatically.