Skip to main content

Bytly Public API

Create, retrieve, update, and delete short links programmatically — the same engine that powers bytly.in itself. REST + JSON, one API key, no OAuth dance.

🔒 API key auth ⚡ 60 req/min 🌐 Public & free 📦 JSON envelope

Overview

The Bytly Public API lets you create and manage short links from your own apps, scripts, or CI pipelines — anything you can send an HTTP request from. Every endpoint is scoped to the owner of the API key used, so a key can only ever see, edit, or delete the links it created.

Base URL

https://bytly.in/api/v1

Response format

Every response — success or failure — uses the same JSON envelope, so client code only needs one code path:

// success
{ "success": true, "data": { ... }, "meta": { ... } }

// error
{ "success": false, "error": { "code": "ALIAS_TAKEN", "message": "'my-launch' is already taken." } }

🔑 Authentication

Every request must include your API key in one of these headers:

X-API-Key: blk_your_key_here

# or

Authorization: Bearer blk_your_key_here

Keys are shown once at creation and stored hashed (SHA-256) server-side — Bytly itself can never see the raw key again. If you lose one, revoke it and generate a new one below. All endpoints are scoped to the key's owner: you can only ever see, edit, or delete your own links.

Create / manage your API keys

🚀 Quickstart

  1. Create an accountSign up at bytly.in — it's free, no credit card required.
  2. Generate an API keyUse the "Authentication" panel above. Copy the key immediately — it's shown only once.
  3. Make your first requestPOST a URL to /api/v1/links with your key in the X-API-Key header (see the Create Link endpoint below).
  4. Use the short linkThe response's shortUrl is live immediately — share it, embed it, redirect with it.

Create a short link

POST/api/v1/links 🔑 Requires API key

Shortens a URL, optionally with a custom alias. Uses the exact same validation as the website's shorten form.

Body parameters

FieldTypeRequiredDescription
urlstringrequiredThe destination URL. Must start with http:// or https://.
aliasstringoptionalCustom short code, 3–30 characters: letters, numbers, - or _ (can't start/end with -/_). Omit for an auto-generated code. Can't be a reserved word or already taken.

Request

curl -X POST https://bytly.in/api/v1/links \
  -H "X-API-Key: blk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/very/long/path", "alias": "my-launch"}'
const res = await fetch("https://bytly.in/api/v1/links", {
  method: "POST",
  headers: {
    "X-API-Key": "blk_your_key_here",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    url: "https://example.com/very/long/path",
    alias: "my-launch"
  })
});
const json = await res.json();
console.log(json.data.shortUrl);
import requests

resp = requests.post(
    "https://bytly.in/api/v1/links",
    headers={"X-API-Key": "blk_your_key_here"},
    json={"url": "https://example.com/very/long/path", "alias": "my-launch"}
)
data = resp.json()
print(data["data"]["shortUrl"])
<?php
$ch = curl_init("https://bytly.in/api/v1/links");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "X-API-Key: blk_your_key_here",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "url" => "https://example.com/very/long/path",
        "alias" => "my-launch"
    ])
]);
$response = json_decode(curl_exec($ch), true);
echo $response["data"]["shortUrl"];
const axios = require("axios");

const { data } = await axios.post(
  "https://bytly.in/api/v1/links",
  { url: "https://example.com/very/long/path", alias: "my-launch" },
  { headers: { "X-API-Key": "blk_your_key_here" } }
);
console.log(data.data.shortUrl);

Response — 201 Created

{
  "success": true,
  "data": {
    "shortCode": "my-launch",
    "shortUrl": "https://bytly.in/my-launch",
    "actualUrl": "https://example.com/very/long/path",
    "isActive": true,
    "isCustomAlias": true,
    "clicks": 0,
    "createdOn": "2026-08-27T10:15:00Z"
  }
}

Possible errors

HTTPCodeCause
400INVALID_URLMissing, or doesn't start with http(s)://
422INVALID_ALIASAlias fails the format rules above
422RESERVED_ALIASAlias is a reserved word
409ALIAS_TAKENAlias already in use

📋 List your links

GET/api/v1/links 🔑 Requires API key

Returns a paginated list of every link owned by the key's user, newest first.

Query parameters

FieldTypeRequiredDescription
pageintegeroptionalPage number, 1-indexed. Defaults to 1.
pageSizeintegeroptionalResults per page. Defaults to 20, capped at 100.

Request

curl "https://bytly.in/api/v1/links?page=1&pageSize=20" \
  -H "X-API-Key: blk_your_key_here"
const res = await fetch("https://bytly.in/api/v1/links?page=1&pageSize=20", {
  headers: { "X-API-Key": "blk_your_key_here" }
});
const json = await res.json();
console.log(json.data, json.meta);
import requests

resp = requests.get(
    "https://bytly.in/api/v1/links",
    headers={"X-API-Key": "blk_your_key_here"},
    params={"page": 1, "pageSize": 20}
)
print(resp.json()["data"])
<?php
$ch = curl_init("https://bytly.in/api/v1/links?page=1&pageSize=20");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["X-API-Key: blk_your_key_here"]
]);
$response = json_decode(curl_exec($ch), true);
print_r($response["data"]);
const axios = require("axios");

const { data } = await axios.get("https://bytly.in/api/v1/links", {
  headers: { "X-API-Key": "blk_your_key_here" },
  params: { page: 1, pageSize: 20 }
});
console.log(data.data, data.meta);

Response — 200 OK

{
  "success": true,
  "data": [
    {
      "shortCode": "my-launch",
      "shortUrl": "https://bytly.in/my-launch",
      "actualUrl": "https://example.com/very/long/path",
      "isActive": true,
      "isCustomAlias": true,
      "clicks": 12,
      "createdOn": "2026-08-27T10:15:00Z"
    }
  ],
  "meta": { "page": 1, "pageSize": 20, "totalItems": 1, "totalPages": 1 }
}

🔍 Retrieve one link

GET/api/v1/links/{code} 🔑 Requires API key

Fetches a single link by its short code.

Path parameters

FieldTypeRequiredDescription
codestringrequiredThe link's short code (the part after bytly.in/).

Request

curl https://bytly.in/api/v1/links/my-launch \
  -H "X-API-Key: blk_your_key_here"
const res = await fetch("https://bytly.in/api/v1/links/my-launch", {
  headers: { "X-API-Key": "blk_your_key_here" }
});
const json = await res.json();
import requests

resp = requests.get(
    "https://bytly.in/api/v1/links/my-launch",
    headers={"X-API-Key": "blk_your_key_here"}
)
print(resp.json())

Response — 200 OK

{
  "success": true,
  "data": {
    "shortCode": "my-launch",
    "shortUrl": "https://bytly.in/my-launch",
    "actualUrl": "https://example.com/very/long/path",
    "isActive": true,
    "isCustomAlias": true,
    "clicks": 12,
    "createdOn": "2026-08-27T10:15:00Z"
  }
}

Possible errors

HTTPCodeCause
404LINK_NOT_FOUNDNo such link, or it belongs to a different account

✏️ Update a link

PUT/api/v1/links/{code} 🔑 Requires API key

Changes the destination URL and/or active status of an existing link. Send only the fields you want to change.

Path parameters

FieldTypeRequiredDescription
codestringrequiredThe link's short code.

Body parameters

FieldTypeRequiredDescription
urlstringoptional*New destination URL. Must start with http:// or https://.
isActivebooleanoptional*Set to false to pause redirects without deleting the link.

* At least one of url or isActive is required.

Request

curl -X PUT https://bytly.in/api/v1/links/my-launch \
  -H "X-API-Key: blk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/new-destination", "isActive": true}'
const res = await fetch("https://bytly.in/api/v1/links/my-launch", {
  method: "PUT",
  headers: {
    "X-API-Key": "blk_your_key_here",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ url: "https://example.com/new-destination", isActive: true })
});
import requests

resp = requests.put(
    "https://bytly.in/api/v1/links/my-launch",
    headers={"X-API-Key": "blk_your_key_here"},
    json={"url": "https://example.com/new-destination", "isActive": True}
)
print(resp.json())

Response — 200 OK

{
  "success": true,
  "data": {
    "shortCode": "my-launch",
    "shortUrl": "https://bytly.in/my-launch",
    "actualUrl": "https://example.com/new-destination",
    "isActive": true,
    "isCustomAlias": true,
    "clicks": 12,
    "createdOn": "2026-08-27T10:15:00Z"
  }
}

Possible errors

HTTPCodeCause
400INVALID_BODYNeither url nor isActive was provided
400INVALID_URLurl doesn't start with http(s)://
404LINK_NOT_FOUNDNo such link, or it belongs to a different account

🗑 Delete a link

DELETE/api/v1/links/{code} 🔑 Requires API key

Permanently deletes a link. This cannot be undone — the short code becomes available again for anyone to claim.

Path parameters

FieldTypeRequiredDescription
codestringrequiredThe link's short code.

Request

curl -X DELETE https://bytly.in/api/v1/links/my-launch \
  -H "X-API-Key: blk_your_key_here"
await fetch("https://bytly.in/api/v1/links/my-launch", {
  method: "DELETE",
  headers: { "X-API-Key": "blk_your_key_here" }
});
import requests

requests.delete(
    "https://bytly.in/api/v1/links/my-launch",
    headers={"X-API-Key": "blk_your_key_here"}
)

Response — 200 OK

{ "success": true, "data": { "message": "Link 'my-launch' deleted." } }

Possible errors

HTTPCodeCause
404LINK_NOT_FOUNDNo such link, or it belongs to a different account

⚠️ Errors

Every error uses the same envelope shown in the Overview. These codes can appear on any endpoint (auth is checked before your request even reaches it):

HTTPCodeMeaning
401MISSING_API_KEYNo key supplied in either header
401INVALID_API_KEYKey is wrong, malformed, or revoked
429RATE_LIMITEDToo many requests — see Retry-After header

Endpoint-specific errors (validation, not-found, conflicts) are documented under each endpoint above.

Rate limits

Each API key is limited to 60 requests per minute, tracked on a rolling (sliding-window) basis — it's not a hard reset every 60 seconds, so bursts are smoothed out rather than all-or-nothing.

Requests without a valid key are limited per IP address instead, so anonymous traffic can never consume a real user's quota.

Going over the limit returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 42

{ "success": false, "error": { "code": "RATE_LIMITED", "message": "Too many requests. Try again in 42s." } }

Always respect the Retry-After header (seconds) before retrying rather than polling immediately.