Node.js (TypeScript) SDK for the Novada API — proxy management, scraping, and wallet endpoints. Zero runtime dependencies (global fetch + Node built-ins), Node.js 18+, ships ESM + CJS with full type declarations.
npm install novadaimport { NovadaClient, Product } from "novada";
const client = new NovadaClient("YOUR_API_KEY"); // or omit to read NOVADA_API_KEY
const list = await client.proxy.whitelist.list({ product: Product.Residential });
console.log(`whitelist total=${list.total}`);The constructor accepts an options object as the second argument:
const client = new NovadaClient("API_KEY", {
baseURL: "https://api-m.novada.com",
webUnblockerURL: "https://webunlocker.novada.com",
scraperURL: "https://scraper.novada.com",
timeout: 30_000, // ms, per attempt
maxRetries: 2,
fetch: customFetch, // optional fetch override
userAgent: "my-app/1.0",
});If no key is passed and NOVADA_API_KEY is also empty, the constructor throws a NovadaError.
Every network method returns a Promise<T> and accepts an optional trailing
{ signal } argument for cancellation (the Node counterpart of Go's
context.Context):
const ac = new AbortController();
setTimeout(() => ac.abort(), 1_000);
await client.wallet.balance({ signal: ac.signal });Novada serves requests from three hosts. All are configurable and default to production:
| Purpose | Default | Used by |
|---|---|---|
| General | https://api-m.novada.com |
Every /v1/* endpoint (proxy, wallet, and the scraper area/balance/unit queries) |
| Web Unblocker | https://webunlocker.novada.com |
scraper.unblocker.scrape, or scraper.do with Target.WebUnblocker |
| Scraper API | https://scraper.novada.com |
scraper.do / scraper.api.* with Target.ScraperAPI |
Only the scrape POST /request calls go to the Web Unblocker / Scraper API
hosts; everything else (/v1/*) uses the general host. The Bearer API key is
injected on every request. Retries apply only to network errors and HTTP 429/5xx
— never to a non-zero business code.
import { NovadaClient, Product } from "novada";
const client = new NovadaClient();
// Sub-accounts
await client.proxy.account.create({
product: Product.Residential, account: "account11", password: "pass11", status: 1,
});
await client.proxy.account.list({ product: Product.Residential });
// Whitelist
await client.proxy.whitelist.add({ product: Product.Residential, ip: "10.10.10.1", remark: "test" });
await client.proxy.whitelist.list({ product: Product.Residential });
// Residential areas & traffic
await client.proxy.residential.countries();
await client.proxy.residential.balance();
await client.proxy.residential.consumeLog({ start: "2025-01-01 00:00:00", end: "2025-01-31 23:59:59" });
// Static ISP / dedicated datacenter
await client.proxy.staticISP.open({ ipType: "normal", region: "hk:1|us-va:2", duration: "week", num: 3 });
await client.proxy.dedicatedDC.list({});Sub-services: account, whitelist, residential, mobile, rotatingISP,
rotatingDC, staticISP, dedicatedDC, unlimited, prohibitDomain.
Required parameters are validated client-side and reported as a
ValidationError (listing every missing field) before any request is sent.
Optional fields that are undefined, "" or 0 are omitted from the request.
import { NovadaClient, Target } from "novada";
const client = new NovadaClient();
// Strongly typed (auto-selects the Scraper API host)
const yt = await client.scraper.api.youtube.videoPost({
url: "https://www.youtube.com/watch?v=HAwTwmzgNc4",
});
console.log(yt.raw); // raw scrape result (JSON/CSV/XLSX, depending on scraper)
// Google Search — typed params, structured result (data.json is the raw result, left as unknown)
const gs = await client.scraper.api.google.search({ query: "apple", country: "us" });
console.log(gs.code, gs.cost_time, gs.data.task_id);
// Generic driver — any scraper_id, choose the host explicitly
const res = await client.scraper.do({
target: Target.ScraperAPI, // or Target.WebUnblocker
scraperName: "youtube.com",
scraperId: "youtube_video-post_explore",
params: [{ url: "https://www.youtube.com/watch?v=HAwTwmzgNc4" }],
returnErrors: true,
});
console.log(res.raw);
// Web Unblocker — typed scrape; returns a structured result, not raw text
const unb = await client.scraper.unblocker.scrape({
targetUrl: "https://www.google.com", // required; responseFormat defaults to "html"
country: "us",
});
console.log(unb.code, unb.html.length, unb.use_balance);
// Query endpoints on the general host
await client.scraper.universal.balance(); // /v1/capture/get_balance
await client.scraper.universal.unit(); // /v1/capture/unit
await client.scraper.unblocker.countries(); // /v1/proxy/unblocker_area
await client.scraper.browser.countries(); // /v1/proxy/browser_areascraper.do JSON-stringifies params into the scraper_params form field,
URL-encodes the body, and routes to the host selected by target. Scrape
responses are returned raw because their format varies by scraper.
scraper.api.google.search and scraper.unblocker.scrape are exceptions: they
send flat form fields and decode the JSON envelope into a structured result.
await client.wallet.balance();
await client.wallet.usageRecord({ page: 1, limit: 20 }); // defaults to 1 / 10Management endpoints return a uniform envelope { code, data, msg, timestamp };
only code === 0 is success. A non-zero code or a non-2xx HTTP status
becomes an APIError (with AuthError / RateLimitError subclasses for the
common HTTP failures).
import { isAuthError, isRateLimited, codeOf } from "novada";
try {
await client.proxy.whitelist.list({ product: Product.Residential });
} catch (err) {
if (isAuthError(err)) { // HTTP 401/403
console.error("invalid API key");
} else if (isRateLimited(err)) { // HTTP 429
console.error("rate limited");
} else {
const code = codeOf(err); // business code, or undefined
console.error(code !== undefined ? `business error code=${code}` : err);
}
}Error classes: NovadaError (base) → APIError (httpStatus, code) →
AuthError / RateLimitError; plus ValidationError (method, fields) for
client-side required-field checks.
The package ships both module formats with type declarations for each:
// ESM
import { NovadaClient } from "novada";// CommonJS
const { NovadaClient } = require("novada");Runnable examples live in examples/: proxy.ts, scraper.ts,
wallet.ts. Set NOVADA_API_KEY and run e.g. npx tsx examples/proxy.ts.