Skip to content
AgentyPublic

About

Shortener.dev is an safe and open-source URL shortener with unlimited click tracking, link analytics, and simple link management.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

shortener.dev

An open-source URL shortener with click analytics, custom back-halves, UTM tracking, geo and app targeting, built on Cloudflare Workers.

Website · Dashboard · Docs · API reference · Report a link

License: AGPLv3 shortener.dev REST API

Links page in the Shortener.dev dashboard

About

Shortener.dev turns a long URL into a short link like srt1.link/spring-sale and records every click: when it happened, where the visitor came from, and which country, browser and device they used.

You can use the hosted version at shortener.dev or run your own copy on Cloudflare. This repository holds the full source: the API, the dashboard and the marketing site.

Features

  • Short links with custom back-halves or random 7-character keys
  • Click analytics by date, country, city, referrer, browser, OS and device
  • Click reports with the raw log of every click
  • Link expiration after a date and time
  • UTM builder to append utm_source, utm_medium, utm_campaign and other parameters
  • Social preview with a custom title, description and image
  • Password-protected links
  • iOS and Android targeting to send mobile visitors to your app
  • Geo targeting to send visitors from a country to a different URL
  • Domain verification by email code or Google Search Console (see Safety)
  • Teams with roles, projects and API keys
  • REST API with an OpenAPI spec and examples in cURL, JavaScript and Python
  • Docs for AI agents at /llms.txt, and every docs page as markdown by adding .md to its URL

Safety

Short links hide their destination, which makes them a favorite tool for phishing and spam. Shortener.dev has several checks so its links stay trustworthy:

  • Each domain is verified before creating a short link. Prove ownership with an email code or Google Search Console. Subdomains are included.
  • Popular sites just work. YouTube, LinkedIn, Amazon and other trusted sites are whitelisted and need no verification.
  • Abused destinations are blocked. Free hosts, tunnels, other shorteners and risky TLDs like .zip are denied (list).
  • Disposable emails are rejected at signup, and signups and password resets are protected by Cloudflare Turnstile and limited per IP address.
  • API requests are rate limited to stop bulk abuse.
  • Accounts and links that break the rules are suspended, and the links stop redirecting - taking customers back to safety.
  • Anyone can report a link.

The result: visitors can trust every link, your brand can't be misused, and the short domain stays off spam blocklists.

Self-hosting? Please keep these checks enabled (assertVerifiedDestinations in domain.service.ts). More: Verify a domain.

API

The REST API lives at https://api.shortener.dev/v1. Create an API key in the dashboard under Settings > API Keys, then send it as a bearer token.

Create a short link

curl -X POST https://api.shortener.dev/v1/links \
  -H "Authorization: Bearer $SHORTENER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/blog/spring-launch",
    "key": "spring-launch"
  }'

Response:

{
  "key": "spring-launch",
  "url": "https://example.com/blog/spring-launch",
  "short_url": "https://srt1.link/spring-launch",
  "is_enabled": true,
  "created_at": "2030-03-01T10:00:00Z"
}

JavaScript

const response = await fetch('https://api.shortener.dev/v1/links', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SHORTENER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com/blog/spring-launch',
    key: 'spring-launch',
  }),
});

if (!response.ok) {
  const { message } = await response.json();
  throw new Error(message);
}

const link = await response.json();
console.log(link.short_url);

Python

import os
import requests

response = requests.post(
    "https://api.shortener.dev/v1/links",
    headers={"Authorization": f"Bearer {os.environ['SHORTENER_API_KEY']}"},
    json={"url": "https://example.com/blog/spring-launch", "key": "spring-launch"},
)
response.raise_for_status()
print(response.json()["short_url"])

Link options

Every option in the dashboard is a field on the same request:

{
  "url": "https://example.com/shop",
  "key": "summer-shop",
  "expire_at": "2030-08-31T23:59:00Z",
  "params": { "utm_source": "newsletter", "utm_medium": "email", "utm_campaign": "summer_sale" },
  "title": "Summer sale, up to 40% off",
  "description": "Our biggest sale of the year ends August 31.",
  "image": "https://example.com/images/summer-og.png",
  "password": "sunny-days",
  "ios": "https://apps.apple.com/us/app/your-app/id000000000",
  "android": "https://play.google.com/store/apps/details?id=com.example.app",
  "geo": [{ "country": "GB", "url": "https://example.co.uk/shop" }]
}

Other endpoints

Method Endpoint Description
GET /v1/links?search=spring&limit=50 List links, newest first
GET /v1/links/{key} Get one link
PUT /v1/links/{key} Update a link
DELETE /v1/links/{key} Delete a link
POST /v1/domains Add a domain
POST /v1/domains/{id}/otp Email a verification code to the domain
POST /v1/domains/{id}/verify Verify the domain with the code
GET /v1/analytics Click analytics
GET /v1/reports Raw click log

Errors return a JSON message. Common status codes: 400 invalid request, 401 missing or invalid API key, 402 plan limit reached, 403 destination domain not verified, 429 rate limited.

The full spec is at api.shortener.dev/openapi.json, and the interactive API reference lets you send test requests.

Repository structure

This is a Turborepo monorepo with npm workspaces.

Path Description Runs on
apps/api REST API (api.shortener.dev): links, domains, analytics, auth, billing, emails Cloudflare Workers + Hono
apps/app Dashboard (app.shortener.dev): React single-page app Cloudflare Workers assets
apps/web Marketing site and docs (shortener.dev): TanStack Start with SSR Cloudflare Workers
packages/eslint-config Shared ESLint config
packages/typescript-config Shared tsconfig.json files
packages/ui Shared React components

Each app has its own README with details: API, Dashboard, Website.

Tech stack

Getting started

Prerequisites

Wrangler is installed with the project dependencies.

1. Clone and install

git clone https://github.com/Agenty/shortener.git
cd shortener
npm install

2. Configure the API

Create apps/api/.dev.vars. See Environment variables in the API README for the full list. The minimum to sign up and create links locally:

ENVIRONMENT=development
JWT_SECRET=a-long-random-string
APP_URL=http://localhost:5173
API_URL=http://localhost:8787
DOMAIN=localhost

3. Create the local database

cd apps/api
npm run sql:dev

This runs schema/query.sql against a local D1 database in .wrangler/.

4. Run everything

From the repository root:

npm run dev
App URL
API http://localhost:8787
Dashboard http://localhost:5173
Website http://localhost:3000

To run a single app, use a filter, for example npx turbo dev --filter=shortener-api.

Other root scripts:

npm run build         # build every app
npm run lint          # lint every app
npm run check-types   # type-check every app
npm run format        # format with Prettier

Deploying to Cloudflare

Each app deploys as its own Worker. The wrangler.json files in this repository point to the resources used by shortener.dev, so replace their IDs with your own.

1. Create the Cloudflare resources

cd apps/api
npx wrangler login
npx wrangler d1 create shortener
npx wrangler kv namespace create KVCACHE
npx wrangler kv namespace create LINKS
npx wrangler r2 bucket create shortener
npx wrangler queues create shortener-queue

Copy the D1 database_id and the two KV id values into apps/api/wrangler.json, in both the top-level config and the previews block.

2. Create the production database

npm run sql

3. Add production secrets

Add each value from .dev.vars as a secret, with production URLs:

npx wrangler secret put JWT_SECRET
npx wrangler secret put APP_URL
# ...repeat for the rest

4. Deploy

# API
cd apps/api && npm run deploy

# Dashboard
cd apps/app && npm run build && npx wrangler deploy

# Website
cd apps/web && npm run deploy

Then add a custom domain to each Worker in the Cloudflare dashboard, and update:

Sponsoring

Shortener.dev is free to use and free to self-host. Sponsorships pay for maintainer time, security fixes, abuse prevention on the hosted service and the infrastructure behind the free tier.

Funding details for foundations and grant programs are published in funding.json, following the funding.json standard.

Contributing

Issues and pull requests are welcome.

  1. Fork the repository and create a branch from development.
  2. Make your change and run npm run lint and npm run check-types.
  3. Open a pull request describing what changed and why.

To report a security issue, email support@shortener.dev instead of opening a public issue.

License

Shortener.dev is built and maintained by Agenty Analytics Pvt. Ltd. as open core: the core product is fully open source under the AGPLv3(LICENSE), and a commercial license is available for organizations that need more than the AGPLv3 allows.

For a commercial license, the hosted Enterprise plan or self-hosting support, contact us at shortener.dev/enterprise.


Built and hosted at shortener.dev, the free URL shortener with click analytics.
Made by Agenty.

About

Shortener.dev is an safe and open-source URL shortener with unlimited click tracking, link analytics, and simple link management.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages