Skip to content

Documentation

Learn how to use opn.onl to shorten links, track analytics, and integrate with your applications.

Getting Started

opn.onl provides a simple REST API for creating and managing short links. You can use it directly via HTTP requests or integrate it into your applications.

# Create a short link (no authentication required)
curl -X POST https://ldr.now/api/links \
  -H "Content-Type: application/json" \
  -d '{"original_url": "https://example.com/very-long-url"}'

API Reference

Authentication

Most endpoints require authentication via Bearer token. Get your token by logging in:

POST https://ldr.now/api/auth/login
{"email": "[email protected]", "password": "yourpassword"}

Include the token in subsequent requests:

Authorization: Bearer your-jwt-token

For scripts and the MCP server, create an API key in Settings → API Keys and send it the same way. A key can be read-only, so it can only read, and it can expire on a date you choose. Otherwise it lasts until you revoke it:

Authorization: Bearer opn_xxxxxxxxxxxx
POST/links

Create a new short link

Request Body

{ "original_url": "https://example.com/page", // Required "custom_alias": "my-link", // Optional (5-25 chars by default) "title": "My Link Title", // Optional, private "password": "secret123", // Optional "expires_at": "2099-12-31T23:59:59Z", // Optional, RFC 3339 "notes": "Internal notes" // Optional }

Also accepted: starts_at (not live before this time), max_clicks, burn_after_reading, safe_link_interstitial, folder_id, tag_ids and org_id. A custom alias uses letters, digits, hyphens and underscores; whoever runs the instance sets its length limits (MIN_ALIAS_LENGTH, MAX_ALIAS_LENGTH).

Response

{ "id": 123, "code": "abc123", "short_url": "https://ldr.now/abc123", "api_url": "https://ldr.now/api/abc123", "original_url": "https://example.com/page", "click_count": 0, "created_at": "2026-01-01T00:00:00Z", "expires_at": "2099-12-31T23:59:59Z", "has_password": true, "is_active": true, "tags": [] // ...plus title, notes, folder_id, org_id, starts_at, max_clicks, // burn_after_reading, burned_at, safe_link_interstitial, bio_visible, bio_label, bio_position, is_pinned }
GET/links

List your links (requires auth)

Query Parameters

  • search - Search by URL, code, title or notes
  • folder_id - Filter by folder
  • tag_id - Filter by tag
  • org_id - List an organization's links instead of your own (members only)
  • limit - Number of results (default and maximum: 1000)
  • offset - Number of links to skip
  • bio_visible - true or false, to keep only links that are or are not on a bio page

The body is a JSON array, pinned links first and then newest first. The X-Total-Count response header says how many links match, ignoring limit and offset.

PUT/links/{id}

Update a link (requires auth)

Send only the fields you want to change. A field you leave out, or send as null, stays as it is. The short code cannot be changed.

Clearing a value

To remove a setting, send its flag as true:

  • remove_password - Drop the password
  • remove_expiration - Clear expires_at
  • remove_starts_at - Clear starts_at
  • remove_max_clicks - Clear max_clicks
  • remove_folder - Take the link out of its folder
  • remove_bio_position - Clear bio_position (wins over a position in the same request)

An empty title, notes or bio_label clears it. bio_label is at most 80 characters and is what the public bio page shows instead of the title. Turning bio_visible off clears bio_position. At most 500 of your own links can be shown at once.

{ "remove_expiration": true, "title": "Renamed" }

Other Endpoints

GET/links/{id}Get one link, in the same shape as an item of the list
PUT/links/{id}Update a link
DELETE/links/{id}Delete a link
GET/links/{id}/statsGet link analytics
GET/links/{id}/qrGenerate QR code — optional ?color=, logo=true, format=svg
GET/links/{id}/rulesList / replace (PUT) smart-routing rules
PUT/auth/bioUpdate your link-in-bio settings
PUT/auth/bio/orderReorder the personal links on your bio page
GET/api/bio/{username}Public bio profile (when enabled)
GET/auth/api-keysList / create (POST) keys; DELETE /auth/api-keys/{id} to revoke
GET/links/exportExport links as CSV

Full interactive API documentation is available at https://ldr.now/api/swagger-ui/

Self-Hosting Guide

opn.onl is fully open-source and can be self-hosted on your own infrastructure. The stack consists of a Rust backend and React frontend.

Requirements

  • Docker and Docker Compose
  • PostgreSQL database
  • Redis (optional, for caching)
  • SMTP server (for email verification, password reset and the contact form)

Quick Start with Docker

# Clone the repository
git clone https://github.com/ysalitrynskyi/opn.onl.git
cd opn.onl

# Copy environment file
cp .env.example .env

# Edit .env with your settings
nano .env

# Start with Docker Compose
docker-compose up -d

Environment Variables

You must set BASE_URL and FRONTEND_URL to your own hosts. Left unset, Compose uses https://opn.onl for both, which is the hosted site. Compose requires POSTGRES_PASSWORD and JWT_SECRET. It passes POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB,POSTGRES_HOST (db) and POSTGRES_PORT (5432) to the backend, not DATABASE_URL.

VariableDescriptionDefault
POSTGRES_USERDatabase userpostgres
POSTGRES_PASSWORDDatabase passwordRequired
POSTGRES_DBDatabase nameopn_onl
JWT_SECRETSecret for JWT tokens (at least 32 bytes)Required
BASE_URLPublic URL of this instance. Set this to your own hosthttps://opn.onl
FRONTEND_URLPublic URL of this instance's site. Set this to your own hosthttps://opn.onl
REDIS_URLRedis connection (optional)-
SMTP_HOSTSMTP server hostname-
MIN_ALIAS_LENGTHMinimum custom alias length5
MAX_ALIAS_LENGTHMaximum custom alias length25 in the Compose files, 50 if unset
ENABLE_URL_SANITIZATIONBlock malicious URLstrue
ENABLE_QR_BRANDINGBrand colour / logo / SVG on QR codestrue
ENABLE_BURN_AFTER_READINGOne-time / self-destruct linkstrue
ENABLE_SAFE_LINK_INTERSTITIALDestination preview + reputation before redirecttrue
ENABLE_CONDITIONAL_ROUTINGRoute by device / OS / country / language + A/Btrue
ENABLE_LINK_IN_BIOOpt-in public link-in-bio profile pagestrue
ENABLE_API_KEYSPersonal API keys for the MCP server / API clientstrue

For complete setup instructions, see the GitHub repository.

Features

Custom Aliases

Create memorable short links

Analytics

Track clicks, locations, devices

Password Protection

Secure sensitive links

Expiration Dates

Auto-expire links

Folders

Organize your links on the dashboard

Tags

Label and filter links on the dashboard

Teams

Collaborate with others, in the app

GeoIP Analytics

See where clicks come from

Get Help

Need Custom Development?

Whether you need help setting up your own instance, custom integrations, or additional features - I'm here to help!

Built with ❤️ by Yevhen Salitrynskyi