A lightweight serverless URL shortener API, backed by Firebase Realtime Database.
Built on Cloudflare Workers and developed with Wrangler.
This project is intended for personal use and small-scale deployments, and runs with minimal resource usage.
- π Key features
- π API access
- π Available endpoints
- π Authentication
- π₯οΈ Developer documentation
- βοΈ License
- π― Author
- Rate limiting β daily request quotas and burst traffic protection (anti-spam).
- No duplicates β prevents storing identical URLs, saving database space.
- No sign-up β no account creation, credit card, or personal data required.
- Privacy-conscious β built with privacy in mind, with GDPR principles considered where relevant.
- Highly configurable β customize behavior to your needs.
- Firebase backend β stores URL mappings in Firebase Realtime Database.
- Minimal REST API β fast, efficient, and lightweight.
- Serverless β runs on the Cloudflare Workers free plan with strict resource limits.
| Endpoint | Rate limit | Maintainer |
|---|---|---|
| https://nsh.nde-code.workers.dev/ | 1 req/IP/sec, 10 new links/IP/day | Me |
CORS is enabled only for the URL-posting endpoint, for clear security reasons.
π‘ Check the status page if you experience latency or other issues while using the public online instance.
Notes:
- Feel free to use the public instance, but be aware of the limits.
- Keep an eye on the repository to catch any changes to these limits.
- The Firebase RTDB database is located in Belgium on the public instance, so users from distant countries may experience some latency.
- Smart Placement routing is enabled for a better experience.
- The rate-limiting system temporarily processes IP addresses, which are pseudonymized using a hash combined with a secret salt before being used for rate limiting. For burst protection, the hashed value is temporarily stored in Cloudflare Workers Cache, while daily limits use Cloudflare Workers KV. The hashed value is retained only for the time required to enforce these limits and is automatically removed afterward.
βΉοΈ In this section,
https://your-worker.org.workers.dev/is used in the cURL command examples. If you've deployed your own instance of the project, replace it with your instance's domain. Otherwise, use the free public instance athttps://nsh.nde-code.workers.dev/, as explained above.
Create a short URL from a long URL. Saves to database and applies rate limiting.
Request body:
| Field | Type | Description |
|---|---|---|
long_url |
string | Required. Original URL to shorten (must be valid) |
Note: request fails if JSON contains unexpected fields or URL exceeds max length.
Response codes:
| Code | Description |
|---|---|
201 |
URL successfully shortened and saved |
200 |
URL already shortened previously (returns existing short link) |
400 |
Invalid body, missing long_url, unexpected field, or invalid URL |
409 |
Hash collision (different URL, same hash) |
429 |
Rate limit exceeded (time-based or daily write limit) |
500 |
Server error (config, environment, or generation failure) |
503 |
KV quota exceeded or database read failure |
507 |
Firebase entry limit reached |
Example request:
curl -X POST "https://your-worker.org.workers.dev/post-url" \
-H "Content-Type: application/json" \
-d '{"long_url": "https://nde-code.github.io/"}'Example response:
{
"success": "https://your-worker.org.workers.dev/url/11i7yev0000000"
}Redirect to the original long URL using the short code.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
code |
string | Required. Unique short ID |
Response codes:
| Code | Description |
|---|---|
301 |
Permanent redirect (verified link) |
302 |
Temporary redirect (unverified link) |
400 |
No valid ID in path |
404 |
Link not found in database |
500 |
Server error |
503 |
Request timeout or storage connection failure |
Example request:
curl -i "https://your-worker.org.workers.dev/url/11i7yev0000000"Retrieve a paginated list of shortened links.
π Security: requires a valid admin key (see authentication).
Query parameters:
| Parameter | Type | Description |
|---|---|---|
count |
number | Number of links to retrieve (default: config value, max: restricted) |
cursor |
string | Last item key from previous page (use next_cursor from response) |
Response codes:
| Code | Description |
|---|---|
200 |
Successfully returned URLs |
400 |
Invalid count or cursor parameter |
401 |
Invalid or missing API key |
429 |
Rate limit exceeded |
500 |
Server error |
503 |
Database retrieval failure |
Example request:
curl "https://your-worker.org.workers.dev/urls?count=2" \
-H "x-api-key: YOUR_ADMIN_KEY"Example response:
{
"urls": {
"11i7yev0000000": {
"long_url": "https://nde-code.github.io/",
"post_date": "2024-05-12T10:00:00.000Z",
"is_verified": true
},
"vgsyqs00000000": {
"long_url": "https://www.google.com/",
"post_date": "2024-05-12T11:30:00.000Z",
"is_verified": false
}
},
"next_cursor": "vgsyqs00000000",
"has_more": true
}Mark a shortened URL as verified.
π Security: requires a valid admin key (see authentication).
Path parameters:
| Parameter | Type | Description |
|---|---|---|
code |
string | Required. Unique short ID |
Response codes:
| Code | Description |
|---|---|
200 |
Link verified successfully (or already verified) |
400 |
No valid ID in path |
401 |
Invalid or missing admin key |
404 |
Link not found |
429 |
Rate limit exceeded |
500 |
Server error |
503 |
Database update failure |
Example request:
curl -X PATCH "https://your-worker.org.workers.dev/verify/11i7yev0000000" \
-H "x-api-key: YOUR_ADMIN_KEY"Remove a shortened URL and decrement the counter.
π Security: requires a valid admin key (see authentication).
Path parameters:
| Parameter | Type | Description |
|---|---|---|
code |
string | Required. Unique short ID |
Response codes:
| Code | Description |
|---|---|
200 |
Link deleted successfully |
400 |
No valid ID in path |
401 |
Invalid or missing admin key |
404 |
Link not found |
429 |
Rate limit exceeded |
500 |
Server error |
503 |
Database deletion failure |
Example request:
curl -X DELETE "https://your-worker.org.workers.dev/delete/11i7yev0000000" \
-H "x-api-key: YOUR_ADMIN_KEY"Recalculate and sync the metadata counter to match the actual URLs in Firebase. Useful for fixing race conditions or desynchronization.
π Security: requires a valid admin or monitoring key (see authentication).
Note: the admin key can be used to manually resynchronize the counter when needed. The monitoring key is also accepted, allowing the endpoint to be called automatically by external monitoring tools (as with
/health) or scheduled services.
Response codes:
| Code | Description |
|---|---|
200 |
Counter resynced successfully (returns new count) |
401 |
Invalid or missing admin key |
429 |
Rate limit exceeded |
500 |
Server error |
503 |
Database communication failure |
Example request:
curl -X PATCH "https://your-worker.org.workers.dev/sync-counter" \
-H "x-api-key: YOUR_ADMIN_KEY"Example response:
{
"success": "Counter synchronized successfully.",
"new_count": 42
}Check service health: configuration, database connectivity, counter integrity, capacity, and KV storage.
π Security: requires a valid monitoring key (see authentication).
Response codes:
| Code | Description |
|---|---|
200 |
All systems operational |
206 |
Degraded but operational (one or more non-critical issues) |
503 |
Service unavailable (critical failure) |
Example request:
curl -X GET "https://your-worker.org.workers.dev/health" \
-H "x-api-key: YOUR_MONITORING_KEY"Example response (healthy):
{
"status": "healthy",
"timestamp": "2026-04-26T20:17:27.121Z",
"checks": {
"config_valid": true,
"firebase_reachable": true,
"counter_accessible": true,
"kv_store_available": true
},
"message": "All systems operational."
}Protected endpoints require either header format:
Authorization: Bearer <MONITORING_or_ADMIN_KEY>x-api-key: <MONITORING_or_ADMIN_KEY>
β Note: trying to access the administration endpoints on the public instance is completely forbidden.
For setup, configuration, and deployment using the Wrangler CLI, see the developer guide.
This project is licensed under the Apache License v2.0.
Created and maintained by Nde-Code.
Don't hesitate to open an issue or a pull request if you have any questions or would like to contribute.