npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@short.io/client-node

v3.2.0

Published

Official Short.io Node.js SDK for URL shortening, link management, QR codes, analytics, and geographic targeting

Readme

Short.io Node.js SDK

Official Node.js SDK for the Short.io URL shortening and link management API.

Table of Contents

Features

  • Link Management - Create, update, delete, duplicate, and archive short links
  • Domain Management - Handle custom domains and their settings
  • QR Code Generation - Generate QR codes for single links or in bulk
  • Bulk Operations - Create, delete, or archive up to 1000 links at once
  • Geographic Targeting - Set country and region-specific redirects
  • Link Analytics - Get detailed link statistics and expand information
  • Folder Organization - Organize links into folders
  • OpenGraph Control - Customize link preview metadata
  • Permissions Management - Control user access to links
  • Full TypeScript Support - Comprehensive type definitions included
  • Modern ESM - Built with ES modules
  • Automatic Rate Limit Handling - Built-in retry logic for 429 responses with exponential backoff

Requirements

  • Node.js 18.0.0 or higher
  • npm or yarn package manager

Installation

npm install @short.io/client-node

Or with yarn:

yarn add @short.io/client-node

Quick Start

1. Get Your API Key

Get your API key from the Short.io dashboard: Integrations & API

2. Initialize the SDK

import { setApiKey } from "@short.io/client-node";

setApiKey("YOUR_API_KEY");

Or using environment variables:

export SHORT_IO_API_KEY="your-api-key"
import { setApiKey } from "@short.io/client-node";

setApiKey(process.env.SHORT_IO_API_KEY);

3. Create Your First Short Link

import { createLink } from "@short.io/client-node";

const result = await createLink({
  body: {
    originalURL: "https://example.com/very-long-url",
    domain: "your-domain.com",
    path: "custom-path", // Optional
    title: "My Link"     // Optional
  }
});

console.log("Short URL:", result.data.shortURL);
console.log("Link ID:", result.data.idString);

Usage Examples

Link Management

Create a Link

import { createLink } from "@short.io/client-node";

const result = await createLink({
  body: {
    originalURL: "https://example.com",
    domain: "your-domain.com",
    path: "my-link",           // Optional: custom path
    title: "Example Link",     // Optional: link title
    tags: ["marketing"],       // Optional: tags for organization
    expiresAt: "2025-12-31",   // Optional: expiration date
    password: "secret123"      // Optional: password protection
  }
});

Get Link Information

import { getLink, expandLink } from "@short.io/client-node";

// Get link by ID
const linkInfo = await getLink({
  path: { linkId: "your-link-id" }
});

console.log("Original URL:", linkInfo.data.originalURL);
console.log("Clicks:", linkInfo.data.clicks);

// Get link by domain and path
const expanded = await expandLink({
  query: {
    domain: "your-domain.com",
    path: "my-link"
  }
});

Update a Link

import { updateLink } from "@short.io/client-node";

const updated = await updateLink({
  path: { linkId: "your-link-id" },
  body: {
    originalURL: "https://new-destination.com",
    title: "Updated Title"
  }
});

Delete a Link

import { deleteLink } from "@short.io/client-node";

await deleteLink({
  path: { link_id: "your-link-id" }
});

Duplicate a Link

import { duplicateLink } from "@short.io/client-node";

const duplicate = await duplicateLink({
  path: { linkId: "your-link-id" },
  body: {
    path: "new-custom-path" // Optional: specify new path
  }
});

Archive/Unarchive Links

import { archiveLink, unarchiveLink } from "@short.io/client-node";

// Archive a link
await archiveLink({
  body: { link_id: "your-link-id" }
});

// Unarchive a link
await unarchiveLink({
  body: { link_id: "your-link-id" }
});

List Links

import { listLinks } from "@short.io/client-node";

const links = await listLinks({
  query: {
    domain_id: "your-domain-id",
    limit: 50,
    offset: 0
  }
});

console.log("Total links:", links.data.length);

Domain Management

import {
  listDomains,
  getDomain,
  createDomain,
  updateDomainSettings
} from "@short.io/client-node";

// List all domains
const domains = await listDomains();
console.log("Domains:", domains.data);

// Get specific domain
const domain = await getDomain({
  path: { domainId: "your-domain-id" }
});

// Create a new domain
const newDomain = await createDomain({
  body: {
    hostname: "links.example.com"
  }
});

// Update domain settings
await updateDomainSettings({
  path: { domainId: "your-domain-id" },
  body: {
    hideReferer: true,
    cloaking: false
  }
});

Bulk Operations

Create Multiple Links

import { createLinksBulk } from "@short.io/client-node";

// Create up to 1000 links at once
const bulkResult = await createLinksBulk({
  body: [
    { originalURL: "https://example1.com", domain: "your-domain.com" },
    { originalURL: "https://example2.com", domain: "your-domain.com", path: "custom" },
    { originalURL: "https://example3.com", domain: "your-domain.com", title: "Third Link" }
  ]
});

// Results array contains link objects or error objects
bulkResult.data.forEach((item, index) => {
  if (item.shortURL) {
    console.log(`Link ${index}: ${item.shortURL}`);
  } else {
    console.log(`Link ${index} failed:`, item.error);
  }
});

Delete Multiple Links

import { deleteLinksBulk } from "@short.io/client-node";

await deleteLinksBulk({
  body: {
    links: ["link-id-1", "link-id-2", "link-id-3"]
  }
});

Archive Multiple Links

import { archiveLinksBulk, unarchiveLinksBulk } from "@short.io/client-node";

// Archive multiple links
await archiveLinksBulk({
  body: { links: ["link-id-1", "link-id-2"] }
});

// Unarchive multiple links
await unarchiveLinksBulk({
  body: { links: ["link-id-1", "link-id-2"] }
});

Bulk Tagging

import { addTagsBulk } from "@short.io/client-node";

await addTagsBulk({
  body: {
    links: ["link-id-1", "link-id-2"],
    tag: "marketing-campaign"
  }
});

QR Codes

import { generateQrCode, generateQrCodesBulk } from "@short.io/client-node";

// Generate QR code for a single link
const qrCode = await generateQrCode({
  path: { linkIdString: "your-link-id" },
  body: {
    size: 300,
    format: "png"  // or "svg"
  }
});

// Bulk QR code generation (rate limited: 1 request per minute)
const bulkQr = await generateQrCodesBulk({
  body: {
    links: ["link-id-1", "link-id-2"],
    size: 200,
    format: "svg"
  }
});

Geographic Targeting

Country Targeting

import {
  createLinkCountry,
  getLinkCountries,
  deleteLinkCountry,
  createLinkCountriesBulk
} from "@short.io/client-node";

// Set country-specific redirect
await createLinkCountry({
  path: { linkId: "your-link-id" },
  body: {
    country: "US",
    url: "https://us-specific-page.com"
  }
});

// Get all country redirects for a link
const countries = await getLinkCountries({
  path: { linkId: "your-link-id" }
});

// Remove country targeting
await deleteLinkCountry({
  path: {
    linkId: "your-link-id",
    country: "US"
  }
});

// Bulk country targeting
await createLinkCountriesBulk({
  path: { linkId: "your-link-id" },
  body: [
    { country: "US", url: "https://us.example.com" },
    { country: "UK", url: "https://uk.example.com" },
    { country: "DE", url: "https://de.example.com" }
  ]
});

Region Targeting

import {
  createLinkRegion,
  getLinkRegions,
  getRegionsByCountry,
  createLinkRegionsBulk
} from "@short.io/client-node";

// Get available regions for a country
const regions = await getRegionsByCountry({
  path: { country: "US" }
});

// Set region-specific redirect
await createLinkRegion({
  path: { linkId: "your-link-id" },
  body: {
    country: "US",
    region: "CA",  // California
    url: "https://california.example.com"
  }
});

// Get all region redirects for a link
const linkRegions = await getLinkRegions({
  path: { linkId: "your-link-id" }
});

// Bulk region targeting
await createLinkRegionsBulk({
  path: { linkId: "your-link-id" },
  body: [
    { country: "US", region: "CA", url: "https://ca.example.com" },
    { country: "US", region: "NY", url: "https://ny.example.com" }
  ]
});

Folders

import {
  listFolders,
  getFolder,
  createFolder
} from "@short.io/client-node";

// List all folders for a domain
const folders = await listFolders({
  path: { domainId: "your-domain-id" }
});

// Get specific folder
const folder = await getFolder({
  path: {
    domainId: "your-domain-id",
    folderId: "your-folder-id"
  }
});

// Create a new folder
const newFolder = await createFolder({
  body: {
    domainId: "your-domain-id",
    name: "Marketing Links"
  }
});

OpenGraph

import {
  getLinkOpengraph,
  updateLinkOpengraph
} from "@short.io/client-node";

// Get OpenGraph properties
const og = await getLinkOpengraph({
  path: {
    domainId: "your-domain-id",
    linkId: "your-link-id"
  }
});

// Set OpenGraph properties
await updateLinkOpengraph({
  path: {
    domainId: "your-domain-id",
    linkId: "your-link-id"
  },
  body: {
    title: "Custom Title",
    description: "Custom description for social sharing",
    image: "https://example.com/image.png"
  }
});

Permissions

import {
  getLinkPermissions,
  addLinkPermission,
  deleteLinkPermission
} from "@short.io/client-node";

// Get link permissions
const permissions = await getLinkPermissions({
  path: {
    domainId: "your-domain-id",
    linkId: "your-link-id"
  }
});

// Add user permission
await addLinkPermission({
  path: {
    domainId: "your-domain-id",
    linkId: "your-link-id",
    userId: "user-id"
  },
  body: {
    permission: "read"  // or "write"
  }
});

// Remove user permission
await deleteLinkPermission({
  path: {
    domainId: "your-domain-id",
    linkId: "your-link-id",
    userId: "user-id"
  }
});

API Reference

Link Operations

| Function | Description | Rate Limit | |----------|-------------|------------| | createLink | Create a new short link | 50/s | | listLinks | List all links for a domain | - | | getLink | Get link info by ID | 20/s | | updateLink | Update an existing link | 20/s | | deleteLink | Delete a link | 20/s | | expandLink | Get link info by domain and path | 20/s | | getLinkByOriginalUrl | Get link by original URL (deprecated) | 20/s | | getLinksByUrl | Get all links with same original URL | - | | duplicateLink | Duplicate an existing link | 50/s | | archiveLink | Archive a link | - | | unarchiveLink | Unarchive a link | - | | createLinkPublic | Create link using public API key | 50/s | | createLinkSimple | Create link (GET method) | 50/s | | createExampleLinks | Generate example links | 5/10s |

Bulk Operations

| Function | Description | Rate Limit | |----------|-------------|------------| | createLinksBulk | Create up to 1000 links | 5/10s | | deleteLinksBulk | Delete multiple links | 1/s | | archiveLinksBulk | Archive multiple links | - | | unarchiveLinksBulk | Unarchive multiple links | - | | addTagsBulk | Add tag to multiple links | - |

Domain Operations

| Function | Description | |----------|-------------| | listDomains | List all domains | | getDomain | Get domain details | | createDomain | Create a new domain | | updateDomainSettings | Update domain settings |

QR Code Operations

| Function | Description | Rate Limit | |----------|-------------|------------| | generateQrCode | Generate QR code for a link | - | | generateQrCodesBulk | Generate QR codes in bulk | 1/min |

Geographic Targeting

| Function | Description | |----------|-------------| | getLinkCountries | Get country redirects | | createLinkCountry | Set country redirect | | createLinkCountriesBulk | Set multiple country redirects | | deleteLinkCountry | Remove country redirect | | getLinkRegions | Get region redirects | | createLinkRegion | Set region redirect | | createLinkRegionsBulk | Set multiple region redirects | | deleteLinkRegion | Remove region redirect | | getRegionsByCountry | List available regions |

Folder Operations

| Function | Description | |----------|-------------| | listFolders | List folders for a domain | | getFolder | Get folder details | | createFolder | Create a new folder |

OpenGraph Operations

| Function | Description | |----------|-------------| | getLinkOpengraph | Get OpenGraph properties | | updateLinkOpengraph | Set OpenGraph properties |

Permission Operations

| Function | Description | |----------|-------------| | getLinkPermissions | Get link permissions | | addLinkPermission | Add user permission | | deleteLinkPermission | Remove user permission |

Advanced Configuration

Custom Client Configuration

import { client } from "@short.io/client-node";

client.setConfig({
  baseUrl: "https://api.short.io",
  headers: {
    "User-Agent": "MyApp/1.0.0",
    "authorization": "your-api-key"
  }
});

Using a Custom Client Instance

import { createClient } from "@short.io/client-node";
import { createLink } from "@short.io/client-node";

const customClient = createClient({
  baseUrl: "https://api.short.io",
  headers: {
    authorization: "your-api-key"
  }
});

const result = await createLink({
  client: customClient,
  body: {
    originalURL: "https://example.com",
    domain: "your-domain.com"
  }
});

TypeScript Support

The SDK provides comprehensive TypeScript definitions for all operations:

import {
  CreateLinkData,
  CreateLinkResponse,
  ListLinksResponse,
  type Options
} from "@short.io/client-node";

// Typed request
const linkData: CreateLinkData = {
  body: {
    originalURL: "https://example.com",
    domain: "your-domain.com",
    path: "typed-link"
  }
};

// Typed response
const result: CreateLinkResponse = await createLink(linkData);

if (result.data) {
  console.log(result.data.shortURL);  // TypeScript knows this is a string
  console.log(result.data.idString);  // TypeScript knows all available properties
}

Response Types

All functions return a response object with the following structure:

interface Response<T> {
  data?: T;           // Response data on success
  error?: unknown;    // Error details on failure
  response: Response; // Raw fetch Response object
}

Rate Limits

| Endpoint Category | Rate Limit | |-------------------|------------| | Link Creation | 50 requests/second | | Link Updates/Deletes | 20 requests/second | | Link Info | 20 requests/second | | Bulk Operations | 5 requests per 10 seconds | | Bulk Delete | 1 request/second | | QR Bulk Generation | 1 request/minute | | Public API | 50 requests/second |

Rate Limit Handling

The SDK provides optional automatic retry logic for HTTP 429 (Too Many Requests) responses with exponential backoff.

Default behavior: No automatic retries. When a 429 response is received, it is returned immediately (or thrown if throwOnError is enabled). You must explicitly enable rate limit handling to get automatic retries.

Enable Global Rate Limiting

import { setApiKey, enableRateLimiting } from "@short.io/client-node";

setApiKey("YOUR_API_KEY");

// Enable with default settings
enableRateLimiting();
// Defaults: maxRetries=3, baseDelayMs=1000, maxDelayMs=60000

// Or customize the behavior
enableRateLimiting({
  maxRetries: 5,           // Maximum retry attempts (default: 3)
  baseDelayMs: 1000,       // Initial delay in ms (default: 1000)
  maxDelayMs: 60000,       // Maximum delay cap in ms (default: 60000)
  onRateLimited: (info) => {
    console.log(`Rate limited. Retry ${info.attempt} in ${info.delayMs}ms`);
    console.log(`Limit: ${info.rateLimitLimit}, Remaining: ${info.rateLimitRemaining}`);
  }
});

Disable Rate Limiting

import { disableRateLimiting } from "@short.io/client-node";

disableRateLimiting();

Per-Request Rate Limiting

Create a rate-limited client for specific requests:

import { createRateLimitedClient, createLink } from "@short.io/client-node";

const client = createRateLimitedClient({
  maxRetries: 5,
  onRateLimited: (info) => console.log(`Retry ${info.attempt}...`)
});

const result = await createLink({
  client,
  body: {
    originalURL: "https://example.com",
    domain: "your-domain.com"
  }
});

Rate Limit Info

The onRateLimited callback receives detailed information:

interface RateLimitInfo {
  status: number;            // HTTP status (429)
  attempt: number;           // Current retry attempt (1, 2, 3...)
  delayMs: number;           // Delay before next retry in ms
  retryAfter?: number;       // Seconds from Retry-After header
  rateLimitLimit?: number;   // Request limit per window (X-RateLimit-Limit)
  rateLimitRemaining?: number; // Remaining requests (X-RateLimit-Remaining)
  rateLimitReset?: number;   // Unix timestamp when limit resets (X-RateLimit-Reset)
  request: Request;          // The request that was rate limited
}

Configuration Options

| Option | Default | Description | |--------|---------|-------------| | maxRetries | 3 | Maximum number of retry attempts before returning the 429 response | | baseDelayMs | 1000 | Initial delay in milliseconds for exponential backoff | | maxDelayMs | 60000 | Maximum delay cap in milliseconds (1 minute) | | onRateLimited | undefined | Optional callback invoked before each retry |

Backoff Strategy

The SDK uses exponential backoff with jitter:

  1. If Retry-After header is present, uses that value (capped at maxDelayMs)
  2. Otherwise, calculates delay as: baseDelayMs * 2^(attempt-1) + random jitter
  3. All delays are capped at maxDelayMs

After exhausting all retries, the 429 response is returned normally (or thrown if throwOnError is enabled).

Error Handling

import { createLink } from "@short.io/client-node";

const result = await createLink({
  body: {
    originalURL: "https://example.com",
    domain: "your-domain.com"
  }
});

if (result.error) {
  // Handle specific error codes
  switch (result.response.status) {
    case 400:
      console.error("Bad request - check your parameters");
      break;
    case 401:
      console.error("Unauthorized - check your API key");
      break;
    case 403:
      console.error("Forbidden - insufficient permissions");
      break;
    case 404:
      console.error("Not found - resource doesn't exist");
      break;
    case 409:
      console.error("Conflict - link with this path already exists");
      break;
    case 429:
      console.error("Rate limit exceeded - slow down requests");
      break;
    default:
      console.error("Unexpected error:", result.error);
  }
} else {
  console.log("Success:", result.data.shortURL);
}

Using ThrowOnError

import { createLink } from "@short.io/client-node";

try {
  const result = await createLink({
    throwOnError: true,
    body: {
      originalURL: "https://example.com",
      domain: "your-domain.com"
    }
  });

  console.log("Success:", result.data.shortURL);
} catch (error) {
  console.error("Request failed:", error);
}

Support

License

FSL (Functional Source License)