Node.js Integration
Zero-dependency integration using native fetch (Node 18+).
Minimal example (submit + poll)
const API = "https://api.vexsolver.com";
const KEY = "sk_live_...";
const TARGET = "https://example.com";
// 1. Submit
const submit = await fetch(`${API}/api/solve`, {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ url: TARGET }),
});
const { taskId } = await submit.json();
// 2. Poll for the result
let data;
for (let i = 0; i < 60; i++) {
await new Promise(r => setTimeout(r, 2000));
const r = await fetch(`${API}/api/getTaskResult?taskId=${taskId}`, {
headers: { "X-API-Key": KEY },
});
data = await r.json();
if (data.status !== "pending") break;
}
if (!data.success) throw new Error(data.error);
// 3. Use cookies + UA on your real request
const page = await fetch(TARGET, {
headers: {
Cookie: data.cookies,
"User-Agent": data.user_agent, // MUST match
},
});
console.log(page.status, (await page.text()).length);TypeScript types + helper
interface SolveSubmitResponse { taskId: string }
interface SolveResult {
success: boolean;
status?: "pending";
cookies?: string;
cookie_map?: Record<string, string>;
user_agent?: string;
vendor?: string;
elapsed_ms?: number;
cached?: boolean;
error?: string;
}
const API = "https://api.vexsolver.com";
const KEY = process.env.VEXSOLVER_KEY!;
async function solve(opts: {
url: string; proxy?: string; appId?: string; fresh?: boolean;
maxWaitMs?: number;
}): Promise<SolveResult> {
const body: Record<string, unknown> = { url: opts.url };
if (opts.proxy) body.proxy = opts.proxy;
if (opts.appId) body.app_id = opts.appId;
if (opts.fresh) body.fresh = true;
const r = await fetch(`${API}/api/solve`, {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!r.ok) throw new Error(`submit failed: ${r.status}`);
const { taskId } = (await r.json()) as SolveSubmitResponse;
const deadline = Date.now() + (opts.maxWaitMs ?? 120_000);
while (Date.now() < deadline) {
await new Promise(res => setTimeout(res, 2000));
const r = await fetch(`${API}/api/getTaskResult?taskId=${taskId}`, {
headers: { "X-API-Key": KEY },
});
const data: SolveResult = await r.json();
if (data.status !== "pending") {
if (!data.success) throw new Error(data.error || "solve failed");
return data;
}
}
throw new Error("solve timed out");
}With Retry-After on rate limit
async function submitWithRetry(body: Record<string, unknown>, attempts = 3) {
for (let i = 0; i < attempts; i++) {
const r = await fetch("https://api.vexsolver.com/api/solve", {
method: "POST",
headers: { "X-API-Key": "sk_live_...", "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (r.status === 429) {
const wait = parseInt(r.headers.get("Retry-After") || "5") * 1000;
await new Promise(res => setTimeout(res, wait));
continue;
}
if (!r.ok) throw new Error(`submit failed: ${r.status}`);
return r.json(); // → { taskId }
}
throw new Error("rate limited after retries");
}Webhook handler (Express)
Pass callback in the solve body and receive the result without polling:
import express from "express";
const app = express();
app.use(express.json());
app.post("/vexsolver-hook", (req, res) => {
const { taskId, success, cookies, user_agent, error } = req.body;
if (success) {
// Store cookies / UA keyed on taskId, kick off your real request.
console.log("solve OK", taskId, "vendor:", req.body.vendor);
} else {
console.log("solve FAIL", taskId, error);
}
res.sendStatus(200); // Respond 2xx within 10s or VexSolver gives up.
});
app.listen(8080);Polling vs webhook
Use polling for one-off scripts and CLI use. Use webhooks for long-running services that already accept HTTP — it's strictly faster (no idle 2s polls) and uses fewer rate-limit slots.