VexSolver

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.