← n2n.to

HTTP API for URL shortening

Quick start

To create a short link, call /api/shorten with either GET (handy for calling directly from browser-side JS on another domain, CORS is enabled) or POST with a JSON body.

GET /api/shorten

GET https://n2n.to/api/shorten?url=https%3A%2F%2Fexample.com%2Fsome%2Flong%2Fpath

The url query parameter must be URL-encoded (encodeURIComponent). Only http and https schemes are allowed, max length 2048 characters.

POST /api/shorten

POST https://n2n.to/api/shorten
Content-Type: application/json

{"url": "https://example.com/some/long/path"}

Response (both methods)

{
  "shortUrl": "https://n2n.to/fAb1",
  "code": "Ab1",
  "originalUrl": "https://example.com/some/long/path"
}

The short link https://n2n.to/f<code> responds with a 301 redirect to the original URL.

Deduplication

If the exact same URL was already shortened, the API returns the existing code instead of creating a new row. This makes repeated calls (e.g. accidental double form submits, retries, prefetching) safe and keeps the database from filling up with duplicates — which also makes the GET endpoint practically idempotent even though it technically creates data.

Rate limits

WindowLimitScope
1 minute20 requestsburst protection, per IP
24 hours (sliding window)100 requestsdaily quota, per IP

Both limits apply only to link creation (GET/POST /api/shorten). Redirects (/f<code>) are not rate-limited.

Exceeding a limit returns HTTP 429 with a Retry-After header (seconds until reset). Invalid requests (400) do not consume quota. Responses that pass validation include:

Error codes

StatusConditionBody
400Missing URL or longer than 2048 characters{"error": "URL is required"}
400Invalid URL or non-http(s) scheme{"error": "Invalid URL"}
429Exceeded 20/min or 100/day limit{"error": "...", "retryAfter": N}
500Internal server error{"error": "Failed to shorten URL"}

CORS

/api/shorten sends Access-Control-Allow-Origin: *, so it can be called with fetch() directly from a browser on any domain — no backend proxy needed. Preflight OPTIONS requests are handled correctly.

Examples

curl (GET)

curl "https://n2n.to/api/shorten?url=https%3A%2F%2Fexample.com"

curl (POST)

curl -X POST https://n2n.to/api/shorten \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}'

JavaScript (client-side app in the browser)

This is the exact use case GET + CORS were added for: calling the API straight from a third-party site's frontend in response to user input.

async function shorten(longUrl) {
  const res = await fetch(
    'https://n2n.to/api/shorten?url=' + encodeURIComponent(longUrl)
  );
  if (!res.ok) {
    const err = await res.json();
    throw new Error(err.error);
  }
  const data = await res.json();
  return data.shortUrl; // https://n2n.to/fAb1
}

shorten('https://example.com/very/long/link').then(console.log);

Python

import requests

resp = requests.get(
    "https://n2n.to/api/shorten",
    params={"url": "https://example.com/very/long/link"},
)
resp.raise_for_status()
print(resp.json()["shortUrl"])