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-tokenFor 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/linksCreate 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
}/linksList your links (requires auth)
Query Parameters
search- Search by URL, code, title or notesfolder_id- Filter by foldertag_id- Filter by tagorg_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 skipbio_visible-trueorfalse, 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.
/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 passwordremove_expiration- Clearexpires_atremove_starts_at- Clearstarts_atremove_max_clicks- Clearmax_clicksremove_folder- Take the link out of its folderremove_bio_position- Clearbio_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
/links/{id}Get one link, in the same shape as an item of the list/links/{id}Update a link/links/{id}Delete a link/links/{id}/statsGet link analytics/links/{id}/qrGenerate QR code — optional ?color=, logo=true, format=svg/links/{id}/rulesList / replace (PUT) smart-routing rules/auth/bioUpdate your link-in-bio settings/auth/bio/orderReorder the personal links on your bio page/api/bio/{username}Public bio profile (when enabled)/auth/api-keysList / create (POST) keys; DELETE /auth/api-keys/{id} to revoke/links/exportExport links as CSVFull 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 -dEnvironment 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.
| Variable | Description | Default |
|---|---|---|
POSTGRES_USER | Database user | postgres |
POSTGRES_PASSWORD | Database password | Required |
POSTGRES_DB | Database name | opn_onl |
JWT_SECRET | Secret for JWT tokens (at least 32 bytes) | Required |
BASE_URL | Public URL of this instance. Set this to your own host | https://opn.onl |
FRONTEND_URL | Public URL of this instance's site. Set this to your own host | https://opn.onl |
REDIS_URL | Redis connection (optional) | - |
SMTP_HOST | SMTP server hostname | - |
MIN_ALIAS_LENGTH | Minimum custom alias length | 5 |
MAX_ALIAS_LENGTH | Maximum custom alias length | 25 in the Compose files, 50 if unset |
ENABLE_URL_SANITIZATION | Block malicious URLs | true |
ENABLE_QR_BRANDING | Brand colour / logo / SVG on QR codes | true |
ENABLE_BURN_AFTER_READING | One-time / self-destruct links | true |
ENABLE_SAFE_LINK_INTERSTITIAL | Destination preview + reputation before redirect | true |
ENABLE_CONDITIONAL_ROUTING | Route by device / OS / country / language + A/B | true |
ENABLE_LINK_IN_BIO | Opt-in public link-in-bio profile pages | true |
ENABLE_API_KEYS | Personal API keys for the MCP server / API clients | true |
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
