Browse topics

REST API

Create and manage short links from your own code. The API uses JSON over HTTPS.

Base URL: https://nocly.site/api/v1

Authentication

Create a key on the API page. The full key is shown once; store it safely. Send it in the Authorization header:

curl https://nocly.site/api/v1/me \
  -H "Authorization: Bearer sl_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

X-API-Key: <key> is also accepted. Keys act as your account and can be disabled, re-enabled or revoked (permanent) at any time. Keys are stored as one-way hashes.

Endpoints

MethodPathDescription
GET/meYour account and the API limits
GET/domainsDomains you can use for links
GET/linksList your links
POST/linksCreate a link
POST/links/bulkCreate many links at once
GET/links/{id}Get one link
PATCH/links/{id}Update a link (only the fields you send)
DELETE/links/{id}Delete a link
GET/links/{id}/analyticsAnalytics for one link
curl -X POST https://nocly.site/api/v1/links \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1234" \
  -d '{"destination":"https://example.com/sale","alias":"sale","tags":["campaign"]}'
FieldTypeNotes
destinationstringRequired. http or https URL
domainstringHostname from /domains. Defaults to your default domain
aliasstringCustom alias. Omit for a random one
prefixstringPrefix plus random part, when no alias is given
redirect_typeinteger301, 302 (default) or 307
title, notesstringOptional
tagsarrayList of tag names
passwordstringProtect with a password. In PATCH, null removes it
max_clicksintegerClick limit
activates_at, expires_atstringISO 8601, for example 2026-12-31T23:59:00Z
utm_source … utm_contentstringUTM parameters
statusstringPATCH only: active or disabled
allow_duplicatebooleanCreate even if you already shortened this destination

The response is 201 Created with {"data": {link}}. A link object includes id, short_url, alias, domain, destination, final_destination, status, state, clicks, unique_clicks, dates and tags.

GET /links?q=sale&status=active&tag=campaign&sort=clicks&dir=desc&page=1&per_page=50

ParameterValues
qSearch alias, destination and title
statusactive, disabled, scheduled, expired, limit_reached, blocked, protected
domainDomain id
tagTag name
sortcreated, clicks, alias, title, last_click, expires
dirasc or desc
page, per_pagePagination, per_page up to 100

The response contains data and meta (page, per_page, total, pages).

Bulk create

POST /links/bulk with a links array (strings or objects with destination, alias, title, notes, tags) and optional shared options: domain, redirect_type, prefix, tags, activates_at, expires_at, max_clicks, utm_source, utm_medium, utm_campaign, and duplicates (skip, the default, or create).

{"links": ["https://example.com/a", {"destination": "https://example.com/b", "alias": "b-page"}], "tags": ["import"]}

Each item gets its own result (created, duplicate, invalid or failed), so one bad item never blocks the others. The maximum number of items per request is bulk_max_items in the /me response.

Analytics

GET /links/{id}/analytics?range=30d returns totals, a time series and breakdowns by referrer, country, region, city, device, OS, browser and language. range is today, 7d, 30d, 90d, 365d or custom with from and to (YYYY-MM-DD).

Idempotency

Send an Idempotency-Key header (8–128 characters) with POST requests. Retrying with the same key returns the original response with Idempotent-Replayed: true instead of creating a duplicate. Reusing a key with a different body returns 422 idempotency_mismatch; a key that is still being processed returns 409 idempotency_in_progress.

Rate limits

Each key may make 120 requests per minute (configurable by administrators). Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Over the limit you receive 429 with Retry-After.

Errors

All errors use one shape:

{"error": {"code": "validation_failed", "message": "The request has invalid fields.", "request_id": "…", "fields": {"destination": "…"}}}
StatusCodes
400bad_request (invalid JSON or parameters)
401unauthenticated, invalid_api_key, api_key_disabled, api_key_revoked
403account_inactive, link_blocked
404not_found
409alias_taken, duplicate_destination (includes the existing link), idempotency_in_progress
415unsupported_media_type (PATCH requires JSON)
400invalid_idempotency_key
422validation_failed, idempotency_mismatch
429rate_limited
500server_error
503api_disabled, unavailable (maintenance)

Include the request_id when asking an administrator for help; it matches the API request log.