Full TypeScript types, zero runtime dependencies, dual ESM + CJS. Uses the
global fetch — Node.js ≥ 18, browsers, and edge runtimes all work.
npm install inbio-sdkShorten a URL (no account)
import { shorten } from "inbio-sdk";
const link = await shorten("https://example.com/very/long/url");
console.log(link.short_url); // https://in.bio/a8K2pAuthenticated client
Pass a token or set INBIO_API_TOKEN in the environment:
import Inbio from "inbio-sdk";
const client = new Inbio({ token: process.env.INBIO_API_TOKEN });
const created = await client.links.create({
destinationUrl: "https://example.com/sale",
slug: "spring-sale",
tags: ["marketing"],
utm: { source: "newsletter", medium: "email" },
});Request options are camelCase (folderId, perPage, clickLimit); response
models keep the exact API field names (short_url, total_clicks).
Everything else
// List with filters, or iterate every page automatically
const page = await client.links.list({ status: "active", perPage: 50 });
for await (const link of client.links.iterate({ tag: "marketing" })) {
console.log(link.slug, link.total_clicks);
}
// Update, toggle, delete
await client.links.update(42, { title: "Renamed" });
await client.links.disable(42);
await client.links.enable(42);
await client.links.delete(42);
// QR code bytes → file
import { writeFile } from "node:fs/promises";
const qr = await client.links.qr(42, { format: "png", size: 1024 });
await writeFile("qr.png", qr);
// Analytics, folders, tags, usage
const stats = await client.links.analytics(42, { from: "2026-06-01" });
const folders = await client.folders.list();
const tags = await client.tags.list();
const usage = await client.account.usage();Errors
import { RateLimitError, ValidationError } from "inbio-sdk";
try {
await client.links.create({ destinationUrl: url });
} catch (err) {
if (err instanceof ValidationError) console.log(err.errors);
if (err instanceof RateLimitError) console.log(err.retryAfter);
}AuthenticationError (401), AccessError (403, plan or scope),
NotFoundError, ValidationError, EntitlementError, StateConflictError
(409), RateLimitError (429), ServerError — all extend InbioError.
GET requests and Retry-After 429s are retried automatically with backoff.
Webhooks
Verification lives in a separate Node-only entry so the core client stays browser-safe:
import { verify } from "inbio-sdk/webhooks";
// e.g. in an Express handler with the raw body
const event = verify(rawBody, req.headers["x-inbio-signature"], secret);
if (event.event === "link.clicked") {
/* ... */
}Signatures are checked with a constant-time comparison and a 5-minute replay-protection window. See Webhooks for the payload format.