npm package: @houla/sdk
Description: Official TypeScript SDK for Hou.la - Free URL shortener, link shortener, QR code generator. Zero dependencies, lightweight, server-side only and link in bio.
Author: Mikhael GERBET
Latest version: 1.0.4

# @houla/sdk

[![npm version](https://img.shields.io/npm/v/@houla/sdk.svg)](https://www.npmjs.com/package/@houla/sdk)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@houla/sdk)](https://bundlephobia.com/package/@houla/sdk)
[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](https://www.npmjs.com/package/@houla/sdk)
[![CI](https://github.com/MikhaelGerbet/houla-sdk/workflows/CI/badge.svg)](https://github.com/MikhaelGerbet/houla-sdk/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Official TypeScript SDK for [Hou.la](https://hou.la)** - The 100% FREE URL shortener, link shortener & QR code generator.

## Why Hou.la?

- **100% FREE** - No credit card required, no hidden fees, no limits on link creation
- **QR Codes included** - Generate QR codes for any link at no extra cost
- **Analytics** - Track clicks, browsers, devices, countries, and referrers
- **UTM tracking** - Built-in UTM parameter support for marketing campaigns
- **Ephemeral links** - Create self-destructing links (1h to 48h)
- **Custom keys** - Choose your own short URL keys
- **No ads** - Clean, fast redirects without any advertising

## Why this SDK?

- **Zero dependencies** - No bloat, just pure TypeScript
- **Tiny bundle** - Less than 6KB minified + gzipped
- **Full TypeScript support** - Complete type definitions included
- **Dual format** - Works with ESM and CommonJS
- **Server-side only** - Keeps your API key secure

> **Important**: This SDK is designed for **server-side use only**. Never expose your API key in frontend code.

## Installation

```bash
npm install @houla/sdk
# or
yarn add @houla/sdk
# or
pnpm add @houla/sdk
```

## Quick Start

```typescript
import { HoulaClient } from "@houla/sdk";

const houla = new HoulaClient({
  apiKey: process.env.HOULA_API_KEY!, // houla_sk_xxx
});

// Create a short link
const link = await houla.createLink({
  url: "https://example.com/very-long-url",
});

console.log(link.shortUrl);  // https://hou.la/abc4
console.log(link.flashUrl);  // https://hou.la/abc4/f (for QR code tracking)
```

## API Response

When creating a link, the API returns:

```typescript
{
  id: string;
  key: string;           // The short key (e.g., "abc4")
  url: string;           // The destination URL
  shortUrl: string;      // Full short URL (e.g., "https://hou.la/abc4")
  flashUrl: string;      // URL for QR codes (e.g., "https://hou.la/abc4/f")
  isEphemeral?: boolean;
  expiresAt?: Date;
  // ...other properties
}
```

> **Note:** Use `flashUrl` for QR codes to track scans separately from direct link clicks.

### Key Length Generation

When no custom key is provided, the API auto-generates one:

| Link Type | Minimum Length |
|-----------|----------------|
| Ephemeral (temporary) | 3 characters |
| Classic (permanent) | 4 characters |

## Create Link - All Options

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `url` | `string` | **Yes** | The destination URL to shorten |
| `key` | `string` | No | Custom short key (e.g., "promo" for hou.la/promo) |
| `title` | `string` | No | A descriptive title for the link |
| `utm` | `boolean` | No | Enable UTM tracking parameters |
| `utm_id` | `string` | No | UTM campaign ID |
| `utm_source` | `string` | No | UTM source (e.g., "newsletter", "twitter") |
| `utm_medium` | `string` | No | UTM medium (e.g., "email", "social") |
| `utm_campaign` | `string` | No | UTM campaign name |
| `utm_term` | `string` | No | UTM term (for paid search) |
| `utm_content` | `string` | No | UTM content (for A/B testing) |
| `isEphemeral` | `boolean` | No | Create a self-destructing link |
| `ephemeralDuration` | `EphemeralDuration` | No | Duration: `"1h"`, `"6h"`, `"12h"`, `"24h"`, `"48h"` |

### QR Code Options (for `getQRCode`, `getQRCodePng`, `getQRCodeSvg`)

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `width` | `number` | 200 | Width in pixels |
| `margin` | `number` | 4 | Margin around QR code |
| `darkColor` | `string` | "#000000" | QR code color |
| `lightColor` | `string` | "#FFFFFF" | Background color |
| `errorCorrectionLevel` | `"L" \| "M" \| "Q" \| "H"` | "M" | Error correction |
| `format` | `"png" \| "svg"` | "png" | Output format |

### Examples

```typescript
// Basic link
const link = await houla.createLink({
  url: "https://example.com",
});
console.log(link.shortUrl); // https://hou.la/abc4

// With custom key
const link = await houla.createLink({
  url: "https://example.com",
  key: "my-promo",      // custom key
  title: "Summer Sale 2026",
});
console.log(link.shortUrl); // https://hou.la/my-promo

// Ephemeral link (self-destructing)
import { EphemeralDuration } from "@houla/sdk";

const link = await houla.createLink({
  url: "https://example.com/secret",
  isEphemeral: true,
  ephemeralDuration: EphemeralDuration.HOURS_24,
});
// link.expiresAt contains the expiration date

// With UTM parameters for marketing
const link = await houla.createLink({
  url: "https://example.com/landing",
  utm: true,
  utm_source: "newsletter",
  utm_medium: "email",
  utm_campaign: "january_2026",
});

// Generate QR Code separately
const qr = await houla.getQRCodePng(link.key, { width: 300 });
console.log(qr.dataUrl); // data:image/png;base64,...
```

## Other Methods

```typescript
// List all links (paginated)
const links = await houla.getLinks(1, 20);

// Get link by ID or key
const link = await houla.getLinkById("uuid");
const link = await houla.getLinkByKey("my-key");

// Update a link
await houla.updateLink("uuid", { title: "New title" });

// Delete a link
await houla.deleteLink("uuid");

// Check key availability
const { available } = await houla.checkAvailability("my-key");

// Generate QR code for existing link
const png = await houla.getQRCodePng("my-key", { width: 300 });
const svg = await houla.getQRCodeSvg("my-key");
```

## Framework Examples

### Next.js (App Router)

```typescript
// app/api/shorten/route.ts
import { HoulaClient } from "@houla/sdk";
import { NextResponse } from "next/server";

const houla = new HoulaClient({ apiKey: process.env.HOULA_API_KEY! });

export async function POST(request: Request) {
  const { url } = await request.json();
  const link = await houla.createLink({ url });
  const qr = await houla.getQRCodePng(link.key);
  return NextResponse.json({ shortUrl: `https://hou.la/${link.key}`, qr: qr.dataUrl });
}
```

### Express

```typescript
import express from "express";
import { HoulaClient } from "@houla/sdk";

const houla = new HoulaClient({ apiKey: process.env.HOULA_API_KEY! });

app.post("/api/shorten", async (req, res) => {
  const link = await houla.createLink({ url: req.body.url });
  res.json({ shortUrl: `https://hou.la/${link.key}` });
});
```

### NestJS

```typescript
import { Injectable } from "@nestjs/common";
import { HoulaClient } from "@houla/sdk";

@Injectable()
export class LinkService {
  private houla = new HoulaClient({ apiKey: process.env.HOULA_API_KEY! });

  async shorten(url: string) {
    return this.houla.createLink({ url });
  }
}
```

## Comparison with Alternatives

| Feature | Hou.la | Bitly | TinyURL | Rebrandly |
|---------|--------|-------|---------|-----------|
| **Price** | FREE | $35+/mo | $12+/mo | $89+/mo |
| **Link creation** | Unlimited | 10/mo free | Limited | Limited |
| **QR codes** | FREE | Paid | Paid | Paid |
| **Analytics** | FREE | Paid | Paid | Paid |
| **UTM tracking** | FREE | Paid | Paid | Paid |
| **Custom keys** | FREE | Paid | Paid | Paid |
| **API access** | FREE | Paid | Paid | Paid |
| **Ephemeral links** | FREE | No | No | No |

## Get Your FREE API Key

1. Go to [hou.la](https://hou.la) and sign in (free account)
2. Navigate to **Settings** > **API Keys**
3. Create a new API key
4. Copy the key (format: `houla_sk_xxx...`)

## License

MIT - Free for personal and commercial use.

---

**Built with love by [Hou.la](https://hou.la)** - The free URL shortener for everyone.