The core of the API. Every endpoint is under https://in.bio/api/v1 and
requires a bearer token — see the overview for authentication, scopes,
and rate limits.
Every response returns the link resource:
{
"id": 42,
"slug": "spring-sale",
"short_url": "https://in.bio/spring-sale",
"destination_url": "https://example.com/sale",
"title": "Spring sale",
"description": null,
"status": "active",
"redirect_type": 302,
"folder_id": 3,
"tags": ["marketing"],
"expires_at": null,
"click_limit": null,
"has_password": false,
"total_clicks": 1240,
"unique_clicks": 981,
"last_clicked_at": "2026-07-21T18:03:11+00:00",
"created_at": "2026-06-01T09:00:00+00:00",
"updated_at": "2026-07-01T09:00:00+00:00"
}| Field | Type | Description |
|---|---|---|
id |
integer | Stable numeric identifier. |
slug |
string | The short code (the part after the domain). |
short_url |
string | The full short link. |
destination_url |
string | Where the link redirects. |
title, description |
string | null | Optional metadata. |
status |
string | active, disabled, archived, expired, exhausted, blocked, pending_review. |
redirect_type |
integer | 301, 302, or 307. |
folder_id |
integer | null | Folder the link belongs to. |
tags |
string[] | Tag names. |
expires_at |
string | null | ISO 8601 expiry, if set. |
click_limit |
integer | null | Max clicks before the link stops, if set. |
has_password |
boolean | Whether the link is password-protected. |
total_clicks, unique_clicks |
integer | Lifetime click counters. |
last_clicked_at |
string | null | ISO 8601 timestamp of the last click. |
created_at, updated_at |
string | ISO 8601 timestamps. |
List links
GET /api/v1/links · scope links:read
| Query param | Type | Description |
|---|---|---|
search |
string | Matches slug, title, or destination URL. |
folder_id |
integer | Only links in this folder. |
tag |
string | Only links with this tag name. |
status |
string | Filter by status (see the resource table above). |
page |
integer | Page number (default 1). |
per_page |
integer | Items per page, 1–100 (default 25). |
curl "https://in.bio/api/v1/links?search=sale&per_page=25" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"const res = await fetch("https://in.bio/api/v1/links?search=sale", {
headers: {
Authorization: "Bearer YOUR_TOKEN",
Accept: "application/json",
},
});
const { data, meta } = await res.json();import requests
r = requests.get(
"https://in.bio/api/v1/links",
headers={"Authorization": "Bearer YOUR_TOKEN"},
params={"search": "sale", "per_page": 25},
)
links = r.json()["data"]Returns a paginated collection: link resources in data, plus Laravel-style
links and meta objects.
{
"data": [ { "id": 42, "slug": "spring-sale", "...": "link fields" } ],
"links": { "first": "...", "last": "...", "prev": null, "next": "..." },
"meta": { "current_page": 1, "per_page": 25, "total": 57, "last_page": 3 }
}Create a link
POST /api/v1/links · scope links:write
Only destination_url is required.
| Field | Type | Description |
|---|---|---|
destination_url |
string | Required. http/https, max 2048 chars. |
slug |
string | 4–64 chars [a-zA-Z0-9-_]; omitted → random. |
title, description, notes |
string | Optional metadata. |
redirect_type |
integer | 301, 302 (default), or 307. |
folder_id |
integer | Must be one of your folders. |
tags |
string[] | Up to 20; created on the fly. |
utm |
object | { source, medium, campaign, term, content }. |
expires_at, fallback_url |
string | Requires link expiration (Pro+). |
click_limit |
integer | Requires click limits (Pro+). |
password |
string | Requires password protection (Pro+). |
curl -X POST https://in.bio/api/v1/links \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://example.com/sale",
"slug": "spring-sale",
"title": "Spring sale",
"tags": ["marketing"],
"utm": { "source": "newsletter", "medium": "email" }
}'const res = await fetch("https://in.bio/api/v1/links", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
destination_url: "https://example.com/sale",
slug: "spring-sale",
tags: ["marketing"],
}),
});
const link = await res.json();import requests
r = requests.post(
"https://in.bio/api/v1/links",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={
"destination_url": "https://example.com/sale",
"slug": "spring-sale",
"tags": ["marketing"],
},
)
link = r.json()Returns 201 Created with the link resource. Reaching your monthly link quota
returns 422 with error.type = "entitlement".
Bulk create
POST /api/v1/links/bulk · scope links:write · Business
Create up to 100 links in one request. Body: { "links": [ ... ] }, each
object taking the same fields as Create. Rows fail
independently.
curl -X POST https://in.bio/api/v1/links/bulk \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "links": [
{ "destination_url": "https://example.com/a", "slug": "a1" },
{ "destination_url": "https://example.com/b", "slug": "a1" }
] }'{
"data": {
"created": [ { "id": 43, "slug": "a1", "...": "link fields" } ],
"failed": [ { "index": 1, "error": "This slug is already taken." } ]
}
}Retrieve a link
GET /api/v1/links/{id} · scope links:read
curl https://in.bio/api/v1/links/42 \
-H "Authorization: Bearer YOUR_TOKEN"Returns the link resource, or 404 if it doesn’t exist or isn’t yours.
Update a link
PATCH /api/v1/links/{id} · scope links:write
Send any subset of the Create fields plus slug. Editing
destination_url requires the edit-destination feature (Pro+) and
re-triggers a safety scan.
curl -X PATCH https://in.bio/api/v1/links/42 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "title": "Spring sale 2026", "tags": ["marketing", "email"] }'Returns the updated link resource.
Delete a link
DELETE /api/v1/links/{id} · scope links:write
curl -X DELETE https://in.bio/api/v1/links/42 \
-H "Authorization: Bearer YOUR_TOKEN"Returns 204 No Content.
Enable / disable
POST /api/v1/links/{id}/enable · scope links:write
POST /api/v1/links/{id}/disable · scope links:write
Toggle a link between active and disabled. A disabled link stops
redirecting but keeps its slug and stats.
curl -X POST https://in.bio/api/v1/links/42/disable \
-H "Authorization: Bearer YOUR_TOKEN"Returns the updated link resource. Toggling from any other state returns 409
with error.type = "state" and the current status.
QR image
GET /api/v1/links/{id}/qr · scope links:read
Returns the link’s QR code as a raw image, using whatever design is saved for the link.
| Query param | Type | Default |
|---|---|---|
format |
png, svg |
png |
size |
64–2048 (px) |
link default |
curl "https://in.bio/api/v1/links/42/qr?format=svg&size=1024" \
-H "Authorization: Bearer YOUR_TOKEN" \
-o qr.svg