PHP SDK for the Novada API — proxy management, scraping, and wallet endpoints. PHP 8.1+, PSR-18/PSR-17 HTTP client agnostic (no Guzzle hard dependency).
composer require novada/sdkThe SDK talks HTTP through PSR-18 (psr/http-client) and PSR-17 factories, discovered automatically via php-http/discovery. If your project doesn't already have a PSR-18 client installed, pull in Guzzle:
composer require guzzlehttp/guzzleYou can also inject your own PSR-18 client/PSR-17 factories via ClientConfig — see Configuration below.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use Novada\Client;
use Novada\Proxy\Dto\ListWhitelistParams;
use Novada\Proxy\Product;
$client = new Client('YOUR_API_KEY'); // or null to read NOVADA_API_KEY
$list = $client->proxy->whitelist->list(new ListWhitelistParams(
product: Product::Residential,
));
printf("whitelist total=%d\n", $list->total);use Novada\Client;
use Novada\ClientConfig;
$client = new Client('API_KEY', new ClientConfig(
baseUrl: 'https://api-m.novada.com',
webUnblockerUrl: 'https://webunlocker.novada.com',
scraperUrl: 'https://scraper.novada.com',
timeout: 30.0,
maxRetries: 2,
httpClient: $psr18Client, // optional: inject your own PSR-18 client
requestFactory: $psr17Factory, // optional: inject your own PSR-17 request factory
streamFactory: $psr17Factory, // optional: inject your own PSR-17 stream factory
userAgent: 'my-app/1.0',
));All fields are optional and have production defaults. 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.
Novada serves requests from three hosts. All are configurable via ClientConfig 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 |
$client->scraper->unblocker->scrape() (typed), 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.
use Novada\Proxy\Dto\{AddWhitelistParams, CreateAccountParams, ListAccountParams, ListWhitelistParams,
OpenStaticIspParams, TimeRange};
use Novada\Proxy\Product;
// Sub-accounts
$client->proxy->account->create(new CreateAccountParams(
product: Product::Residential, account: 'account11', password: 'pass11', status: 1,
));
$client->proxy->account->list(new ListAccountParams(product: Product::Residential));
// Whitelist
$client->proxy->whitelist->add(new AddWhitelistParams(
product: Product::Residential, ip: '10.10.10.1', remark: 'test',
));
$client->proxy->whitelist->list(new ListWhitelistParams(product: Product::Residential));
// Residential areas & traffic
$client->proxy->residential->countries();
$client->proxy->residential->balance();
$client->proxy->residential->consumeLog(new TimeRange(
start: '2025-01-01 00:00:00', end: '2025-01-31 23:59:59',
));
// Static ISP / dedicated datacenter
$client->proxy->staticIsp->open(new OpenStaticIspParams(
ipType: 'normal', region: 'hk:1|us-va:2', duration: 'week', num: 3,
));
$client->proxy->dedicatedDc->list(new \Novada\Proxy\Dto\ListStaticParams());Sub-services: account, whitelist, residential, mobile, rotatingIsp, rotatingDc, staticIsp, dedicatedDc, unlimited, prohibitDomain. Required parameters are validated client-side and reported as a Novada\Exception\ValidationException before any request is sent.
use Novada\Scraper\Dto\{GoogleSearchParams, Request as ScrapeRequest, UnblockerParams, YouTubeVideoParams};
use Novada\Scraper\Target;
// Strongly typed (auto-selects the Scraper API host)
$res = $client->scraper->api->youtube->videoPost(new YouTubeVideoParams(
url: 'https://www.youtube.com/watch?v=HAwTwmzgNc4',
));
// Google Search (SerpApi) — typed params, structured result ($result->data->json is the raw result array)
$gs = $client->scraper->api->google->search(new GoogleSearchParams(
query: 'apple', country: 'us', // device, hl, renderJs, aiOverview, … optional
));
printf("%d %d\n", $gs->code, $gs->costTime);
// Generic driver — any scraper_id, choose the host explicitly
$res = $client->scraper->do(new ScrapeRequest(
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,
));
echo $res->raw; // raw scrape result (JSON/CSV/XLSX, depending on scraper)
// Web Unblocker — typed scrape; returns a structured result, not raw text
$unb = $client->scraper->unblocker->scrape(new UnblockerParams(
targetUrl: 'https://www.google.com', // required
country: 'us', // responseFormat defaults to "html"
));
printf("%d %d %f\n", $unb->code, strlen($unb->html), $unb->useBalance);
// Query endpoints on the general host
$client->scraper->universal->balance(); // /v1/capture/get_balance
$client->scraper->universal->unit(); // /v1/capture/unit
$client->scraper->unblocker->countries(); // /v1/proxy/unblocker_area
$client->scraper->browser->countries(); // /v1/proxy/browser_area$client->scraper->do() JSON-encodes params, places it in 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. $client->scraper->unblocker->scrape() is the dedicated Web Unblocker call: it sends the endpoint's own fields (target_url, response_format, js_render, country, wait_ms, …) and decodes the JSON envelope into a structured UnblockerResult (html, code, msg, msgDetail, useBalance).
use Novada\Wallet\Dto\UsageRecordParams;
$client->wallet->balance();
$client->wallet->usageRecord(new UsageRecordParams(page: 1, limit: 20));Management 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 a Novada\Exception\ApiException.
use Novada\Exception\ApiException;
use Novada\Exception\AuthException;
use Novada\Exception\RateLimitException;
use Novada\Proxy\Dto\ListWhitelistParams;
use Novada\Proxy\Product;
try {
$list = $client->proxy->whitelist->list(new ListWhitelistParams(product: Product::Residential));
} catch (AuthException $e) { // HTTP 401/403
error_log('invalid API key');
} catch (RateLimitException $e) { // HTTP 429
error_log('rate limited');
} catch (ApiException $e) { // any other API error, business code in getCode()
error_log(sprintf('business error code=%d: %s', $e->getCode(), $e->getMsg()));
}Runnable examples live in examples/: proxy.php, scraper.php, wallet.php. Set NOVADA_API_KEY and run e.g. php examples/proxy.php.
composer install
composer stan # PHPStan, level max
composer cs # PHP-CS-Fixer, dry run
composer cs-fix # PHP-CS-Fixer, apply fixes
composer test # PHPUnit