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.
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
- Create an accountSign up at bytly.in — it's free, no credit card required.
- Generate an API keyUse the "Authentication" panel above. Copy the key immediately — it's shown only once.
- Make your first requestPOST a URL to
/api/v1/linkswith your key in theX-API-Keyheader (see the Create Link endpoint below). - Use the short linkThe response's
shortUrlis live immediately — share it, embed it, redirect with it.
Endpoints
Base URL: https://bytly.in/api/v1
Create a short link
Shortens a URL, optionally with a custom alias. Uses the exact same validation as the website's shorten form.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | The destination URL. Must start with http:// or https://. |
| alias | string | optional | Custom 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
| HTTP | Code | Cause |
|---|---|---|
| 400 | INVALID_URL | Missing, or doesn't start with http(s):// |
| 422 | INVALID_ALIAS | Alias fails the format rules above |
| 422 | RESERVED_ALIAS | Alias is a reserved word |
| 409 | ALIAS_TAKEN | Alias already in use |
List your links
Returns a paginated list of every link owned by the key's user, newest first.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
| page | integer | optional | Page number, 1-indexed. Defaults to 1. |
| pageSize | integer | optional | Results 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
Fetches a single link by its short code.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
| code | string | required | The 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
| HTTP | Code | Cause |
|---|---|---|
| 404 | LINK_NOT_FOUND | No such link, or it belongs to a different account |
Update a link
Changes the destination URL and/or active status of an existing link. Send only the fields you want to change.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
| code | string | required | The link's short code. |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | optional* | New destination URL. Must start with http:// or https://. |
| isActive | boolean | optional* | 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
| HTTP | Code | Cause |
|---|---|---|
| 400 | INVALID_BODY | Neither url nor isActive was provided |
| 400 | INVALID_URL | url doesn't start with http(s):// |
| 404 | LINK_NOT_FOUND | No such link, or it belongs to a different account |
Delete a link
Permanently deletes a link. This cannot be undone — the short code becomes available again for anyone to claim.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
| code | string | required | The 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
| HTTP | Code | Cause |
|---|---|---|
| 404 | LINK_NOT_FOUND | No 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):
| HTTP | Code | Meaning |
|---|---|---|
| 401 | MISSING_API_KEY | No key supplied in either header |
| 401 | INVALID_API_KEY | Key is wrong, malformed, or revoked |
| 429 | RATE_LIMITED | Too 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.