Docs SDK — JavaScript

JavaScript / TypeScript SDK

@ucfp/client is a thin, dependency-free wrapper over the HTTP API. Returns native Promises. Works in Node ≥ 18, Bun, Deno, and the browser (via the demo proxy — never embed a server key in a browser bundle).

Install

npm install @ucfp/client
# or
pnpm add @ucfp/client
# or
bun add @ucfp/client

Quick start

import { UcfpClient } from '@ucfp/client';

const ucfp = new UcfpClient({
  apiKey: process.env.UCFP_KEY!,            // ucfp_…
  baseUrl: 'https://ucfp.dev'               // optional, default
});

const fp = await ucfp.text('The quick brown fox.');
console.log(fp.algorithm, fp.fingerprintBytes);

Text

const fp = await ucfp.text('Hello world.', {
  algorithm: 'minhash',
  h: 128,
  k: 5,
  tokenizer: 'Word',
  canonicalizer: { caseFold: true, normalization: 'Nfkc' }
});

Returns:

type TextFingerprint = {
  recordId: string;
  algorithm: string;
  formatVersion: number;
  configHash: string;
  fingerprintBytes: number;
  hasEmbedding: boolean;
  embeddingDim?: number;
  modelId?: string;
};

Streaming

for await (const fp of ucfp.textStream(asyncIterableOfStrings)) {
  console.log(fp.recordId);
}

Image

import { readFile } from 'node:fs/promises';

const bytes = await readFile('./photo.jpg');
const fp = await ucfp.image(bytes, {
  algorithm: 'multi',
  preprocess: { maxDimension: 2048, minDimension: 32 }
});

In the browser:

async function onUpload(file: File) {
  const fp = await ucfp.image(file, { algorithm: 'phash' });
  console.log(fp.recordId);
}

Audio

const wavBytes = await readFile('./clip.wav');
const fp = await ucfp.audio(wavBytes, {
  algorithm: 'wang',
  sampleRate: 44100
});

Watermark detection (no record persisted):

const report = await ucfp.audioWatermark(wavBytes, { sampleRate: 44100 });
if (report.detected) console.log('Payload:', report.payload, 'conf:', report.confidence);

Records

const meta = await ucfp.getRecord(fp.recordId);
console.log(meta.algorithm, meta.fingerprintBytes);

await ucfp.deleteRecord(fp.recordId);

Errors

Every method returns a typed Promise<…>; failures throw UcfpError:

import { UcfpError } from '@ucfp/client';

try {
  await ucfp.text(input);
} catch (e) {
  if (e instanceof UcfpError) {
    console.error(e.status, e.code, e.message);
    if (e.status === 429) await sleep(e.retryAfterMs ?? 1000);
  }
}

UcfpError fields: status, code (one of the error codes), message, retryAfterMs?, recordId?.

Configuration

new UcfpClient({
  apiKey: '…',                      // required
  baseUrl: 'https://ucfp.dev',      // override for self-host
  fetch: globalThis.fetch,          // override for testing
  timeoutMs: 30_000,                // per-request, default 30s
  retry: { attempts: 3, base: 250 } // exponential backoff for 5xx + 429
});

Browser caveats

Do not ship a server-side apiKey to the browser. For browser usage, point the client at your own backend that proxies to UCFP, or use the public demo path with apiKey: undefined (rate-limited by IP, requires Turnstile token in the body).

const ucfp = new UcfpClient({
  baseUrl: '/api/fingerprint',  // your own proxy
  apiKey: undefined
});

Self-host

Point at your own Rust binary:

const ucfp = new UcfpClient({
  apiKey: 'your-UCFP_TOKEN-value',
  baseUrl: 'https://ucfp.your-company.internal'
});

The client is otherwise identical.

Source

The package is open-source — see github.com/bravo1goingdark/ucfp under clients/js/.