← shortener

zbi.baby api

For bots, scripts and anything that speaks http.

Nothing is stored. A short link is the original link, compressed into the address itself, so shorten and expand are pure functions of their input. No database, no accounts, no rate limit, no expiry and nothing to leak.

Links come in two flavours: #fragment links, which are unpacked in your browser, and /path links (mode=qr), which the site reads from the path and which are therefore safe to print as a QR code.

Try it



  

Endpoints

Method Path Description
GET, POST /api/v1/shorten Compress a link. Parameters: url (required), mode = hash, emoji or qr.
GET /api/v1/expand Unpack a short link. Parameters: link (a full short link, or a bare payload), mode to disambiguate a bare payload.
GET /api/v1/health Liveness check.
GET /api/v1 This list, as JSON.

Shorten

# json in, json out, which is what curl gets
curl -G "https://zbi.baby/api/v1/shorten" \
  --data-urlencode "url=https://www.amazon.com/dp/B0CX23V2ZK/ref=sr_1_1?keywords=widget&qid=1720000000"

# the --data-urlencode is what keeps the ? and & in the link from being read
# as parameters of the api call itself

# one line of text instead of json, for bots
curl -G "https://zbi.baby/api/v1/shorten" --data-urlencode "url=https://example.com/x" -d "format=text"
curl -H "Accept: text/plain" "https://zbi.baby/api/v1/shorten?url=https://example.com/x"

# post it instead, as json
curl -X POST https://zbi.baby/api/v1/shorten \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/some/long/path","mode":"hash"}'

# or as a form, the way a html form would send it
curl -X POST https://zbi.baby/api/v1/shorten -d "url=https://example.com/x"

# qr links keep the payload in the path
curl -G "https://zbi.baby/api/v1/shorten" --data-urlencode "url=https://example.com/x" -d "mode=qr" -d "format=text"

Response, for the amazon link above:

{
  "input": "https://www.amazon.com/dp/B0CX23V2ZK/ref=sr_1_1?keywords=widget&qid=1720000000",
  "short": "https://zbi.baby#j,7*+lgC#nlHXCWK7#Bs!+ZxT*GtbviRyo_Zl@C*/?f@IA$0[wM'Tj&!",
  "payload": "j,7*+lgC#nlHXCWK7#Bs!+ZxT*GtbviRyo_Zl@C*/?f@IA$0[wM'Tj&!",
  "mode": "hash",
  "input_length": 78,
  "payload_length": 56,
  "short_length": 74,
  "shorter_percent": 5
}

Expand

# percent-encode the # so it is not read as a fragment of the api call
curl "https://zbi.baby/api/v1/expand?link=https%3A%2F%2Fzbi.baby%23j%2C7*%2BlgC%23nlHXCWK7%23Bs!%2BZxT*GtbviRyo_Zl%40C*%2F%3Ff%40IA$0%5BwM%27Tj%26%21"

# or let curl do it
curl -G "https://zbi.baby/api/v1/expand" \
  --data-urlencode "link=https://zbi.baby#j,7*+lgC#nlHXCWK7#Bs!+ZxT*GtbviRyo_Zl@C*/?f@IA$0[wM'Tj&!"

# a bare payload works too
curl -G "https://zbi.baby/api/v1/expand" --data-urlencode "payload=OQ/ap#"

A bare qr payload is the one case that needs a hint, because those symbols are indistinguishable from a text payload, and decoding one as the other can still come out looking like a link. Send mode=qr, or put a / in front of the payload, or just send the whole short link. Guessing would mean handing back the wrong link with a 200, so the api answers 400 ambiguous_payload instead.

Response:

{ "url": "https://www.amazon.com/dp/B0CX23V2ZK/ref=sr_1_1?keywords=widget&qid=1720000000" }

Using it from a bot

One GET is all it takes, and there is no key to register. Two things are worth knowing if the caller is a chat bot:

# discord.py
import requests
from discord.ext import commands

@commands.command()
async def shorten(ctx, url: str):
    response = requests.get(
        "https://zbi.baby/api/v1/shorten",
        params={"url": url, "mode": "qr"},
        headers={"Accept": "text/plain"},
        timeout=5,
    )
    response.raise_for_status()
    await ctx.send(response.text.strip())
// discord.js
const query = new URLSearchParams({ url, mode: "qr", format: "text" });
const response = await fetch(`https://zbi.baby/api/v1/shorten?${query}`);
if (response.ok) {
  await interaction.reply((await response.text()).trim());
}

On Cloudflare, two settings will bite you: keep Bot Fight Mode off for /api, because it challenges requests that look like bots and a bot asking for a short link is exactly that; and check that the Worker route really does win over Pages for /api, or requests will land on the static site and come back as HTML instead of JSON.

Errors

Anything that goes wrong answers with a matching status and a code:

{
  "error": {
    "code": "unsupported_protocol",
    "message": "Only http and https links can be compressed, got \"ftp:\"."
  }
}

Rules

Links

Source code · zbi.baby · fork of ha.mr