Node.js recipes
All examples use native fetch (Node 18+). No npm dependencies required.
End-to-end submit + poll
import fs from "node:fs";
const API_BASE = "https://platform.zenhire.ai";
const API_KEY = process.env.ZENHIRE_API_KEY;
async function analyze(audioPath, externalId) {
// 1. Submit
const form = new FormData();
form.append("audio", new Blob([fs.readFileSync(audioPath)]), audioPath);
form.append("language", "en");
if (externalId) form.append("externalId", externalId);
const submitRes = await fetch(`${API_BASE}/api/v1/speech/analyze`, {
method: "POST",
headers: { "X-API-Key": API_KEY },
body: form,
});
if (!submitRes.ok) throw new Error(`Submit failed: ${submitRes.status}`);
const { id: requestId } = await submitRes.json();
console.log(`Submitted: ${requestId}`);
// 2. Poll
const start = Date.now();
let interval = 15_000;
while (Date.now() - start < 20 * 60_000) {
await new Promise((r) => setTimeout(r, interval));
const res = await fetch(
`${API_BASE}/api/v1/speech/analyze/${requestId}`,
{ headers: { "X-API-Key": API_KEY } }
);
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get("Retry-After") || "15", 10);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
continue;
}
if (!res.ok) throw new Error(`Poll failed: ${res.status}`);
const data = await res.json();
if (data.status === "success" || data.status === "partial") return data;
if (data.status === "failed") {
throw new Error(`${data.error?.code}: ${data.error?.message}`);
}
// Escalate: 15s → 30s → 60s
const elapsed = Date.now() - start;
interval = elapsed < 120_000 ? 15_000 : elapsed < 300_000 ? 30_000 : 60_000;
}
throw new Error("Polling budget exhausted");
}
const result = await analyze("interview.mp3", "candidate-abc-123");
console.log(`Overall: ${result.scores.overall} (${result.scores.cefrLevel})`);
List runs
const res = await fetch(
"https://platform.zenhire.ai/api/v1/speech/runs?limit=50&status=success",
{ headers: { "X-API-Key": process.env.ZENHIRE_API_KEY } }
);
const { runs, pagination } = await res.json();
console.log(`${runs.length} successful runs, hasMore=${pagination.hasMore}`);