
Bright Data JavaScript SDK providing easy and scalable methods for scraping, web search, datasets, and more.
npm install @brightdata/sdk1. Signup and get your API token
import{bdclient}from'@brightdata/sdk';constclient=newbdclient({apiKey: '[your_api_token]',// or set BRIGHTDATA_API_TOKEN env variable});// Scrape a webpageconsthtml=awaitclient.scrapeUrl('https://example.com');console.log(html);// Search the webconstresults=awaitclient.search.google('pizza restaurants',{country: 'us'});console.log(results);Don't forget to close when done:
awaitclient.close();- Web Scraping — Scrape any website using anti-bot detection bypass and proxy support
- Search Engine Results — Google, Bing, and Yandex search with batch support
- Platform Scrapers — Structured data collection from LinkedIn, Amazon, Instagram, TikTok, YouTube, Reddit, and more
- Crawl API — Crawl any URL(s) and get every output format (markdown, HTML, text) bundled per page
- Discover API — AI-powered web search with intent-based relevance ranking
- Scraper Studio — Trigger and fetch results from custom scrapers built in Bright Data's Scraper Studio
- Browser API — CDP WebSocket URLs for connecting Playwright, Puppeteer, or Selenium to Bright Data's cloud browsers
- Datasets — Access 126 pre-built datasets across dozens of platforms with query/download support
- Parallel Processing — Concurrent processing for multiple URLs or queries
- Robust Error Handling — Typed error classes with retry logic
- Zone Management — Automatic zone creation and management
- Multiple Output Formats — HTML, JSON, Markdown, and screenshots
- Dual Build — Both ESM and CommonJS supported
- TypeScript — Fully typed API with overloaded signatures
- Subpath Exports — Tree-shakeable imports via
@brightdata/sdk/scrapers,@brightdata/sdk/search,@brightdata/sdk/datasets
// Single URL — returns HTML string by defaultconsthtml=awaitclient.scrapeUrl('https://example.com');// Multiple URLs (parallel processing)constresults=awaitclient.scrapeUrl(['https://example1.com','https://example2.com',]);// Get markdown contentconstmd=awaitclient.scrapeUrl('https://example.com',{dataFormat: 'markdown',});// Get structured JSONconstdata=awaitclient.scrapeUrl('https://example.com',{format: 'json',});// Take a screenshotconstscreenshot=awaitclient.scrapeUrl('https://example.com',{dataFormat: 'screenshot',});// Full optionsconstresult=awaitclient.scrapeUrl('https://example.com',{format: 'raw',// 'raw' (default) or 'json'dataFormat: 'html',// 'html' (default), 'markdown' (alias: 'md'), 'screenshot'country: 'gb',// two-letter country codemethod: 'GET',// HTTP method});// Google searchconstresults=awaitclient.search.google('pizza restaurants');// Bing searchconstresults=awaitclient.search.bing('pizza restaurants');// Yandex searchconstresults=awaitclient.search.yandex('pizza restaurants');// Batch search (parallel)constresults=awaitclient.search.google(['pizza','sushi','tacos']);// With optionsconstresults=awaitclient.search.google('pizza',{country: 'gb',format: 'json',});Note: If
countryis not specified, requests exit through an arbitrary proxy location, so results may be geo-located to an unexpected country. Always passcountrywhen you need consistent, localized results.
Collect structured data from popular platforms. Each platform supports sync collection (collect*) and async orchestrated scraping (trigger, poll, download).
// LinkedIn profilesconstdata=awaitclient.scrape.linkedin.collectProfiles(['https://www.linkedin.com/in/satyanadella/'],{format: 'json'},);// Amazon productsconstdata=awaitclient.scrape.amazon.collectProducts(['https://www.amazon.com/dp/B0D77BX8Y4'],{format: 'json'},);// Instagram profilesconstdata=awaitclient.scrape.instagram.collectProfiles(['https://www.instagram.com/natgeo/'],{format: 'json'},);// TikTok profilesconstdata=awaitclient.scrape.tiktok.collectProfiles(['https://www.tiktok.com/@tiktok'],{format: 'json'},);// YouTube videosconstdata=awaitclient.scrape.youtube.collectVideos(['https://www.youtube.com/watch?v=dQw4w9WgXcQ'],{format: 'json'},);// Reddit postsconstdata=awaitclient.scrape.reddit.collectPosts(['https://www.reddit.com/r/technology/top/'],{format: 'json'},);Orchestrated scraping (async trigger → poll → download):
constresult=awaitclient.scrape.linkedin.profiles(['https://www.linkedin.com/in/satyanadella/'],{pollInterval: 5000,pollTimeout: 180_000},);console.log(result.data);// structured dataconsole.log(result.status);// 'ready'console.log(result.rowCount);Available platforms:linkedin, amazon, instagram, tiktok, youtube, reddit, facebook, pinterest, chatGPT, digikey, perplexity
Crawl one or more URLs and get every output format (markdown, HTML, text) bundled per page.
// Sync — single round-tripconstresult=awaitclient.crawler.crawl('https://example.com');console.log(result.data[0].markdown);// Batchconstresult=awaitclient.crawler.crawl(['https://example.com','https://example.com/about',]);console.log(`${result.pageCount} pages`);// Async — trigger, poll, downloadconstjob=awaitclient.crawler.trigger('https://example.com');conststatus=awaitclient.crawler.status(job.snapshotId);constresult=awaitclient.crawler.download(job.snapshotId);AI-powered web search with relevance ranking based on intent.
discover() resolves to a DiscoverResult wrapper (not a bare array). The items
are on result.data (or its alias result.results), and the result is iterable.
On failure result.success is false, result.error carries the reason, and
result.data / result.results stay an empty array — so iterating never throws.
// Basic searchconstresult=awaitclient.discover('artificial intelligence trends 2026');if(!result.success){console.error('discover failed:',result.error);}else{console.log(result.results);// [{ link, title, description, relevance_score }, ...]for(constitemofresult){console.log(`[${item.relevance_score}] ${item.title}`);}}// With intent for semantic rankingconstresult=awaitclient.discover('Tesla battery technology',{intent: 'recent breakthroughs in EV battery chemistry',});// With filtering and localizationconstresult=awaitclient.discover('sustainable fashion brands',{intent: 'eco-friendly clothing companies',filterKeywords: ['sustainability','eco-friendly','organic'],country: 'us',numResults: 10,});// Include full page contentconstresult=awaitclient.discover('python asyncio tutorial',{includeContent: true,numResults: 3,});// Manual trigger/poll/fetchconstjob=awaitclient.discoverTrigger('market research SaaS',{intent: 'competitor pricing strategies',});awaitjob.wait({timeout: 60_000});constdata=awaitjob.fetch();Trigger and fetch results from your custom scrapers built in Scraper Studio.
// Orchestrated — trigger + poll + return resultsconstresults=awaitclient.scraperStudio.run('c_your_collector_id',{input: {url: 'https://example.com/product/1'},});// results: RunResult[] — one per input with { input, data, error, responseId, elapsedMs }// Multiple inputs (processed sequentially)constresults=awaitclient.scraperStudio.run('c_your_collector_id',{input: [{url: 'https://example.com/product/1'},{url: 'https://example.com/product/2'},],});// Manual control — trigger, then poll yourselfconstjob=awaitclient.scraperStudio.trigger('c_your_collector_id',{url: 'https://example.com/product/1',});constdata=awaitjob.waitAndFetch();// Check job status (by job ID from the dashboard)conststatus=awaitclient.scraperStudio.status('j_abc123');console.log(status.status);// 'queued' | 'running' | 'done' | 'failed'Build CDP WebSocket URLs for connecting Playwright, Puppeteer, or Selenium to Bright Data's cloud browsers. Credentials come from browserUsername/browserPassword options or BRIGHTDATA_BROWSERAPI_USERNAME/BRIGHTDATA_BROWSERAPI_PASSWORD env vars.
// Get a connection URLconsturl=client.browser.getConnectUrl();// Geo-target the browser with a 2-letter country codeconstusUrl=client.browser.getConnectUrl({country: 'us'});// Connect with Playwrightimport{chromium}from'playwright';constbrowser=awaitchromium.connectOverCDP(url);constpage=awaitbrowser.newPage();awaitpage.goto('https://example.com');consthtml=awaitpage.content();awaitbrowser.close();Access 126 pre-built datasets for querying and downloading structured data snapshots.
constds=client.datasets;// List all datasets available on your accountconstlist=awaitds.list();// Get field metadata for a datasetconstmeta=awaitds.instagramProfiles.getMetadata();console.log(meta.fields);// [{ name, type, description }, ...]// Query a dataset (triggers a snapshot)constsnapshotId=awaitds.instagramProfiles.query({url: 'https://www.instagram.com/natgeo/'},{records_limit: 10},);// Check snapshot statusconststatus=awaitds.instagramProfiles.getStatus(snapshotId);console.log(status.status);// 'running' | 'ready' | ...// Download when readyconstrows=awaitds.instagramProfiles.download(snapshotId);Available datasets:
| Platform | Datasets |
|---|---|
linkedinProfiles, linkedinCompanies | |
| Amazon | amazonProducts, amazonReviews, amazonSellers, amazonBestSellers, amazonProductsSearch, amazonProductsGlobal, amazonWalmart |
instagramProfiles, instagramPosts, instagramComments, instagramReels | |
| TikTok | tiktokProfiles, tiktokPosts, tiktokComments, tiktokShop |
| X/Twitter | xTwitterPosts, xTwitterProfiles |
constdata=awaitclient.scrapeUrl('https://example.com');constfilePath=awaitclient.saveResults(data,{filename: 'results.json',format: 'json',});console.log(`Saved to: ${filePath}`);Node.js (>= 20) is the supported and tested runtime. The SDK also runs under Bun without crashing, but with a real caveat: Bun's bundled undici provides only a bare-bones Agent (no compose(), no close(), no custom dispatch), and Bun's request()/stream() ignore the dispatcher option entirely, routing through Bun's own native HTTP client instead. In practice this means requests still succeed under Bun, but none of Transport's tuning — connection pooling, keep-alive, custom timeouts, automatic retry on 429/500/502/503/504, and DNS caching — has any effect there; Bun's own defaults apply instead. The SDK detects the missing capabilities automatically (never crashes construction or close()), rather than failing.
Get your API token from Bright Data Control Panel.
Already logged in with the CLI? The SDK works with no configuration. If you've run brightdata login with the Bright Data CLI, the SDK automatically picks up those stored credentials. The token is resolved in this order:
apiKeypassed tonew bdclient({ apiKey })BRIGHTDATA_API_TOKEN(orBRIGHTDATA_API_KEY) environment variable- Credentials stored by the CLI (
brightdata login)
If none are found, the client throws with instructions to log in or set a token.
BRIGHTDATA_API_TOKEN=your_api_token# BRIGHTDATA_API_KEY also acceptedBRIGHTDATA_WEB_UNLOCKER_ZONE=your_web_unlocker_zone# OptionalBRIGHTDATA_SERP_ZONE=your_serp_zone# OptionalBRIGHTDATA_BROWSERAPI_USERNAME=your_browser_username# Optional, for Browser APIBRIGHTDATA_BROWSERAPI_PASSWORD=your_browser_password# Optional, for Browser APIBRIGHTDATA_VERBOSE=1# Optional, enable verbose loggingTip: When loading these from a
.envfile withnode --env-file=.env, note that variables already set in your shell take precedence over the file.
constclient=newbdclient({apiKey: 'string',// API token (or use BRIGHTDATA_API_TOKEN env var)timeout: 120000,// Request timeout in ms (1000–300000)autoCreateZones: true,// Auto-create zones if they don't existwebUnlockerZone: 'string',// Custom web unlocker zone nameserpZone: 'string',// Custom SERP zone namelogLevel: 'INFO',// 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL'structuredLogging: true,// Use structured JSON loggingverbose: false,// Enable verbose loggingrateLimit: 0,// Max requests per period (0 = unlimited)ratePeriod: 1000,// Rate limit period in ms});The client maintains HTTP connections. Always close when done:
awaitclient.close();// Or use Symbol.asyncDispose (TypeScript 5.2+)awaitusingclient=newbdclient();| Constant | Default | Description |
|---|---|---|
DEFAULT_CONCURRENCY | 10 | Max parallel tasks |
DEFAULT_TIMEOUT | 120000 | Request timeout (milliseconds) |
MAX_RETRIES | 3 | Retry attempts on failure |
RETRY_BACKOFF_FACTOR | 1.5 | Exponential backoff multiplier |
constzones=awaitclient.listZones();console.log(`Found ${zones.length} zones`);For tree-shaking or importing only what you need:
import{ScrapeRouter,LinkedinAPI}from'@brightdata/sdk/scrapers';import{SearchRouter}from'@brightdata/sdk/search';import{DatasetsClient,BaseDataset}from'@brightdata/sdk/datasets';The SDK exports typed error classes that extend BRDError:
import{bdclient,ValidationError,AuthenticationError,BRDError}from'@brightdata/sdk';try{constresult=awaitclient.scrapeUrl('https://example.com');}catch(error){if(errorinstanceofValidationError){console.error('Invalid input:',error.message);}elseif(errorinstanceofAuthenticationError){console.error('Auth failed:',error.message);}elseif(errorinstanceofBRDError){console.error('SDK error:',error.message);}}Error types:ValidationError, AuthenticationError, ZoneError, NetworkError, NetworkTimeoutError, TimeoutError, APIError, DataNotReadyError, FSError
| Problem | Fix |
|---|---|
npm.ps1 cannot be loaded because running scripts is disabled (PowerShell) | Use cmd instead of PowerShell, or run npm.cmd <command>, or run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned. If overridden by Group Policy, contact your IT team. |
SELF_SIGNED_CERT_IN_CHAIN during npm install | Your network uses SSL inspection. Point npm to your corporate root certificate: npm config set cafile "C:\path\to\corporate-root.cer" |
| Certificate errors at runtime | Set the env variable NODE_EXTRA_CA_CERTS=C:\path\to\corporate-root.cer so Node trusts your corporate certificate. |
If you get this error, check the following in order:
- Verify the token itself works, outside the SDK:
If this fails, generate a new API key with admin permissions in the control panel. Note the SDK needs an account-level API token — not a zone password.
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.brightdata.com/zone/get_active_zones - Check for shell overrides. Environment variables set in your shell take
precedence over
.envfiles loaded withnode --env-file=.env. Runset BRIGHTDATA(Windows) orenv | grep BRIGHTDATA(macOS/Linux) and clear any leftover values. - Check your
.envfile: no quotes, no spaces around=, no trailing whitespace, and the token copied exactly (including dashes). Prefer LF line endings. - Print what actually loaded:Hidden characters like
console.log(JSON.stringify(process.env.BRIGHTDATA_API_TOKEN));
\rwill be visible in the output.
For any issues, contact Bright Data support, or open an issue in this repository.
This project is licensed under the MIT License.