npm package: @htpdf/client
Description: Official TypeScript SDK for the HTPDF API — generate, transform, and manage PDFs at scale.
Author: HassTech
Latest version: 1.0.0

# @htpdf/client

Official TypeScript SDK for the [HTPDF API](https://htpdf.net) — generate, transform, and manage PDFs at scale.

- **TypeScript-first** with full type exports
- **Zero runtime dependencies** — uses native `fetch`
- **Automatic retries** with exponential backoff on 429 / 5xx
- **Dual-format** — ships ESM and CommonJS builds

## Installation

```bash
npm install @htpdf/client
# or
pnpm add @htpdf/client
# or
yarn add @htpdf/client
```

Requires Node.js 18+ (native `fetch`).

## Quick Start

```typescript
import HTPDF from '@htpdf/client';

const htpdf = new HTPDF('htk_live_abc123');

// Generate a PDF from HTML
const pdf = await htpdf.pdf.fromHtml({
  html: '<h1>Hello World</h1>',
  options: { format: 'A4' },
});

console.log(pdf.url);   // download URL
console.log(pdf.pages);  // number of pages
```

## Configuration

```typescript
const htpdf = new HTPDF('htk_live_abc123', {
  baseUrl: 'https://htpdf.net',  // default
  timeout: 60_000,                // 60s (default: 30s)
  maxRetries: 5,                  // default: 3
});
```

## API Reference

### PDF Generation

#### `htpdf.pdf.fromHtml(params)`

Generate a PDF from raw HTML.

```typescript
const pdf = await htpdf.pdf.fromHtml({
  html: '<h1>Invoice #1234</h1><p>Amount: $99.00</p>',
  brand_kit_id: 'bk_abc',  // optional
  options: {
    format: 'A4',
    margin: { top: '20mm', bottom: '20mm' },
    printBackground: true,
  },
});
```

#### `htpdf.pdf.fromUrl(params)`

Generate a PDF by rendering a web page.

```typescript
const pdf = await htpdf.pdf.fromUrl({
  url: 'https://example.com/report',
  options: { format: 'Letter', landscape: true },
});
```

#### `htpdf.pdf.fromTemplate(params)`

Generate a PDF from a named template with injected data.

```typescript
const pdf = await htpdf.pdf.fromTemplate({
  template: 'invoice',
  data: { customer: 'Acme Corp', total: 1500 },
});
```

#### `htpdf.pdf.fromMarkdown(params)`

Generate a PDF from Markdown content.

```typescript
const pdf = await htpdf.pdf.fromMarkdown({
  markdown: '# Report\n\nKey findings...',
  theme: 'github',
});
```

#### `htpdf.pdf.batch(params)`

Generate multiple PDFs in a single request.

```typescript
const batch = await htpdf.pdf.batch({
  items: [
    { source: 'html', html: '<h1>Doc 1</h1>' },
    { source: 'url', url: 'https://example.com' },
    { source: 'template', template: 'invoice', data: { total: 100 } },
  ],
});

console.log(`${batch.succeeded}/${batch.total} succeeded`);
batch.items.forEach(pdf => console.log(pdf.url));
```

### PDF Operations

#### `htpdf.pdf.get(id)`

Retrieve a previously generated PDF.

```typescript
const pdf = await htpdf.pdf.get('pdf_abc123');
```

#### `htpdf.pdf.info(id)`

Get metadata about a PDF.

```typescript
const info = await htpdf.pdf.info('pdf_abc123');
console.log(info.pages, info.size);
```

#### `htpdf.pdf.merge(params)`

Merge multiple PDFs into one.

```typescript
const merged = await htpdf.pdf.merge({
  sources: [{ pdf_id: 'pdf_abc' }, { pdf_id: 'pdf_def' }],
});
```

#### `htpdf.pdf.split(params)`

Split a PDF by page ranges.

```typescript
const result = await htpdf.pdf.split({
  pdf_id: 'pdf_abc',
  ranges: ['1-3', '4-6'],
});
result.parts.forEach(part => console.log(part.url));
```

#### `htpdf.pdf.compress(params)`

Compress a PDF.

```typescript
const compressed = await htpdf.pdf.compress({ pdf_id: 'pdf_abc' });
console.log(`Saved ${(compressed.savings * 100).toFixed(1)}%`);
```

#### `htpdf.pdf.rotate(params)`

Rotate pages in a PDF.

```typescript
const rotated = await htpdf.pdf.rotate({
  pdf_id: 'pdf_abc',
  rotation: 90,
  pages: [1, 3],
});
```

### Screenshots

#### `htpdf.screenshots.fromHtml(params)`

```typescript
const shot = await htpdf.screenshots.fromHtml({
  html: '<div style="background: blue; width: 800px; height: 600px;"></div>',
  options: { format: 'png', fullPage: true },
});
```

#### `htpdf.screenshots.fromUrl(params)`

```typescript
const shot = await htpdf.screenshots.fromUrl({
  url: 'https://example.com',
  options: { viewportWidth: 1280, viewportHeight: 720, format: 'jpeg', quality: 85 },
});
```

### Brand Kits

```typescript
// List all
const { data: kits } = await htpdf.brandKits.list();

// Create
const kit = await htpdf.brandKits.create({
  name: 'Acme Corp',
  primary_color: '#0066FF',
  logo_url: 'https://acme.com/logo.svg',
});

// Get
const kit = await htpdf.brandKits.get('bk_abc');

// Update
await htpdf.brandKits.update('bk_abc', { primary_color: '#FF6600' });

// Delete
await htpdf.brandKits.delete('bk_abc');
```

### Templates

```typescript
// List all templates
const { data: templates } = await htpdf.templates.list();

// Get a specific template
const template = await htpdf.templates.get('invoice');
console.log(template.schema); // expected data fields
```

### Billing

```typescript
// Current usage
const usage = await htpdf.billing.usage();
console.log(`${usage.total_requests} / ${usage.limits.requests} requests`);

// Daily analytics
const analytics = await htpdf.billing.analytics();
analytics.daily.forEach(day => {
  console.log(`${day.date}: ${day.requests} requests, ${day.errors} errors`);
});
```

### QR Codes & Barcodes

```typescript
// QR Code
const qr = await htpdf.generate.qrCode({
  data: 'https://htpdf.net',
  size: 256,
  format: 'svg',
  errorCorrection: 'H',
});

// Barcode
const barcode = await htpdf.generate.barcode({
  data: '978-3-16-148410-0',
  format: 'ean13',
});
```

## Error Handling

The SDK throws typed errors with HTTP status, error code, message, and request ID.

```typescript
import HTPDF, {
  HTPDFError,
  AuthenticationError,
  RateLimitError,
  ValidationError,
} from '@htpdf/client';

try {
  await htpdf.pdf.fromHtml({ html: '' });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.message);    // "html must not be empty"
    console.log(error.code);       // "validation_error"
    console.log(error.status);     // 422
    console.log(error.requestId);  // "req_xyz"
    console.log(error.details);    // additional validation info
  }

  if (error instanceof RateLimitError) {
    console.log(error.rateLimit.remaining); // 0
    console.log(error.retryAfterMs);        // ms until reset
  }

  if (error instanceof AuthenticationError) {
    // Invalid or expired API key
  }

  // Catch-all for any HTPDF error
  if (error instanceof HTPDFError) {
    console.log(error.status, error.code, error.message);
  }
}
```

### Error Classes

| Class | HTTP Status | When |
|-------|-------------|------|
| `AuthenticationError` | 401 | Invalid or missing API key |
| `PermissionError` | 403 | Insufficient permissions |
| `NotFoundError` | 404 | Resource does not exist |
| `ValidationError` | 422 | Invalid request payload |
| `RateLimitError` | 429 | Rate limit exceeded |
| `InternalServerError` | 5xx | Server-side failure |
| `TimeoutError` | — | Request timed out |
| `ConnectionError` | — | Network-level failure |

## Rate Limiting

Rate-limit headers are automatically tracked and exposed after every request:

```typescript
await htpdf.pdf.fromHtml({ html: '<h1>Hi</h1>' });

console.log(htpdf.rateLimit.limit);     // e.g. 100
console.log(htpdf.rateLimit.remaining); // e.g. 98
console.log(htpdf.rateLimit.reset);     // Unix timestamp
```

429 responses are automatically retried with exponential backoff (up to `maxRetries`).

## License

MIT