LinconwavesLinconwavesUnified docs
Minlink

Links API

Create, manage, and analyze Minlink short links with the developer API.

  • Base URL: https://cm.minlink.io/v1/api
  • Auth: x-minlink-api-key: <your-key> (create keys in the console → Developer → API Keys)
  • Responses: { status, message, data }; errors: { status: "error", error: { code, message } }

Quickstart (backend)

const BASE = 'https://cm.minlink.io/v1/api';
const API_KEY = process.env.MINLINK_API_KEY!;

async function createLink() {
  const res = await fetch(`${BASE}/links`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-minlink-api-key': API_KEY,
    },
    body: JSON.stringify({
      originalUrl: 'https://example.com/landing',
      title: 'Launch page',
      tags: ['launch', 'promo'],
      expiration: { type: 'never' },
    }),
  });
  const json = await res.json();
  console.log(json.data.link.shortUrl);
}

createLink().catch(console.error);

Frontend snippets

// app/api/link/route.ts
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const API_KEY = process.env.MINLINK_API_KEY!;
  const BASE = 'https://cm.minlink.io/v1/api';
  const payload = await req.json();

  const res = await fetch(`${BASE}/links`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-minlink-api-key': API_KEY,
    },
    body: JSON.stringify(payload),
  });

  return NextResponse.json(await res.json(), { status: res.status });
}
  • POST /links
  • Body: see the payload map below.
PropTypeWhat it controls
originalUrlstring (required)Destination URL to shorten.
titlestring (required)Friendly name shown in dashboards.
tagsstring[]Optional labels for filtering (e.g., ["launch","promo"]).
expiration

object

{ type: "never" | "date" | "clicks", value?: string | number }
Expiration mode: never, fixed date (value ISO string), or max clicks (value number).
expiresAtstring (ISO)Concrete date when expiration.type === "date".
customDomainstringOverride default domain when building shortUrl.
statusstringOptional lifecycle toggle, defaults to active if omitted.
  • Notes: duplicate originalUrl + customDomain for the same owner returns 409 with LINK_ALREADY_EXISTS.
  • Sample response: data.link includes _id, originalUrl, shortUrl, title, tags, status, expiration, clicks, timestamps.
  • GET /links - returns all links for the API key owner, newest first.
  • GET /links/:id
  • PUT /links/:id
  • Body: any subset of originalUrl, title, tags, expiration, customDomain, expiresAt, status.
  • Custom domains: updating customDomain rewrites shortUrl to https://<new-domain>/r/<code>.
  • DELETE /links/:id

Redirect + public behavior

Consumer-facing redirects are public at https://<frontend>/r/<code>. The API gives you the shortUrl to share; it enforces:

  • expiration by date or click count
  • optional maxClicks
  • optional password (prompts end users if set)

Analytics

  • GET /links/:id/analytics
  • Returns (inside data.analytics):
    • totalClicks, uniqueVisitors (array of visitor fingerprints), uniqueVisitorsCount
    • averageClickTime, averageClicksPerVisitor
    • clicksData (time-series), topCountries, devices, timeOfDay, topReferrers, topBrowsers
    • lastUpdated, lastClickAt

Tracking pixel (auto-enqueued)

This is public-facing and normally used by the redirect flow. It is available at /links/track/:id on the main router; developer API consumers typically do not need to call it directly because clicks are recorded from redirects.

Frontend integration

  • Share the returned shortUrl; redirects from /r/:shortCode handle analytics, expiration, passwords, and max-click rules automatically.
  • If you host a landing page and still want server analytics, embed a 1×1 pixel:
<img
  src="https://cm.minlink.io/v1/links/track/<linkId>?ua=${encodeURIComponent(
    navigator.userAgent,
  )}&screen=${window.innerWidth}x${window.innerHeight}&referrer=${encodeURIComponent(document.referrer)}"
  alt=""
  width="1"
  height="1"
/>
  • Public links: POST /links/p (rate-limited) creates a 30-day link; your UI can poll /links/analyticsUpdate?shortUrl=<full-short-url> to show status/metrics.
  • Reachability check before creation: GET https://cm.minlink.io/v1/links/check-url?url=<encodedUrl>.
  • QR: render the QR endpoint output directly as an <img> or download it for reuse.

QR codes

Fetch QR code image

  • GET /links/:id/qrcode
  • Query params:
    • format: png (default) | jpg | svg
    • size: number (pixels) optional
  • Returns the image buffer.

Error codes to expect

  • 401/403 if the API key is missing, invalid, inactive, suspended owner, or lacks permissions.
  • 402 if the developer account is paused (low balance).
  • 404 if a link is not found.
  • 409 if a duplicate link exists.

Helpful patterns

Create, then fetch analytics

import fetch from 'node-fetch';
const headers = {
  'content-type': 'application/json',
  'x-minlink-api-key': process.env.MINLINK_API_KEY!,
};
const BASE = 'https://cm.minlink.io/v1/api';

async function run() {
  const create = await fetch(`${BASE}/links`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      originalUrl: 'https://example.com',
      title: 'Example',
      expiration: { type: 'never' },
    }),
  }).then((r) => r.json());

  const linkId = create.data.link._id;
  const analytics = await fetch(`${BASE}/links/${linkId}/analytics`, {
    headers,
  }).then((r) => r.json());

  console.log('Short URL:', create.data.link.shortUrl);
  console.log('Top countries:', analytics.data.analytics.topCountries);
}

run().catch(console.error);
await fetch(`https://cm.minlink.io/v1/api/links/${linkId}`, {
  method: 'DELETE',
  headers: { 'x-minlink-api-key': process.env.MINLINK_API_KEY! },
});
Links API | Linconwaves Docs