SafeURL Docs
Integrations

TypeScript / Node.js

Integrate SafeURL into a TypeScript or Node.js application

No SDK required — the API is a standard REST interface. These examples use the native fetch API (Node 18+).

Setup

const SAFEURL_API = "https://api.safeurl.ai";
const API_KEY = process.env.SAFEURL_API_KEY!; // sk_live_...

function safeurl(path: string, init?: RequestInit) {
  return fetch(`${SAFEURL_API}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      ...init?.headers,
    },
  });
}

Single scan

type ScanState =
  | "QUEUED"
  | "FETCHING"
  | "ANALYZING"
  | "COMPLETED"
  | "FAILED"
  | "TIMED_OUT";

interface ScanResult {
  riskScore: number; // 0–100
  confidence: number; // 0–1
  categories: string[];
  reasoning: string;
  indicators: string[];
}

interface ScanJob {
  id: string;
  url: string;
  state: ScanState;
  result?: ScanResult;
  createdAt: string;
}

async function scan(url: string): Promise<ScanJob> {
  const res = await safeurl("/v1/scans", {
    method: "POST",
    body: JSON.stringify({ url }),
  });
  if (!res.ok) throw new Error(`Scan failed: ${res.status}`);
  return res.json() as Promise<ScanJob>;
}

const job = await scan("https://example.com");
console.log(job.id, job.state); // job_01j... QUEUED

Poll until complete

async function waitForScan(
  jobId: string,
  timeoutMs = 60_000,
): Promise<ScanJob> {
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const res = await safeurl(`/v1/scans/${jobId}`);
    const job = (await res.json()) as ScanJob;

    if (job.state === "COMPLETED" || job.state === "FAILED") return job;

    await new Promise((r) => setTimeout(r, 2000));
  }

  throw new Error(`Scan ${jobId} timed out after ${timeoutMs}ms`);
}

const completed = await waitForScan(job.id);
console.log(completed.result?.riskScore); // e.g. 12

Stream state transitions (SSE)

import { EventSource } from "eventsource"; // npm i eventsource

function streamScan(jobId: string): Promise<ScanJob> {
  return new Promise((resolve, reject) => {
    const es = new EventSource(`${SAFEURL_API}/v1/scans/${jobId}/events`, {
      headers: { Authorization: `Bearer ${API_KEY}` },
    });

    es.onmessage = (event) => {
      const job = JSON.parse(event.data) as ScanJob;
      if (job.state === "COMPLETED" || job.state === "FAILED") {
        es.close();
        resolve(job);
      }
    };

    es.onerror = (err) => {
      es.close();
      reject(err);
    };
  });
}

Batch scan

Scan up to 50 URLs in a single request. All jobs share a batchId.

interface BatchResponse {
  batchId: string;
  jobs: ScanJob[];
}

async function batchScan(urls: string[]): Promise<BatchResponse> {
  const res = await safeurl("/v1/scans/batch", {
    method: "POST",
    body: JSON.stringify({ urls }),
  });
  if (!res.ok) throw new Error(`Batch scan failed: ${res.status}`);
  return res.json() as Promise<BatchResponse>;
}

const { batchId, jobs } = await batchScan([
  "https://example.com",
  "https://suspicious-site.xyz",
]);

// Poll all jobs
const results = await Promise.all(jobs.map((j) => waitForScan(j.id)));

On this page