Read this before changing anything. Every line below was verified against the
live API or the installed SDK, brightdata-sdk 2.5.2, on 2026-09-18. Several
lines contradict the SDK's own behaviour or docstrings.
LinkedIn profile data is personal data. The API says so itself: of the 46 fields
in the profiles dataset it marks 8 with pii: true, namely id, name,
about, url, input_url, linkedin_id, first_name and last_name.
- Examples use public figures whose professional presence is already public, and company pages. Do not add an example that scrapes a private individual.
- Do not add an example built on
search.linkedin.profiles. It searches by first and last name, which is people search. - Permitted use is the customer's responsibility, and the README links to Bright Data's compliance material rather than restating it here.
- The SDK reads
BRIGHTDATA_API_TOKENfrom the environment, from a.envfile found by searching upward from its own install folder (so the project root, when the virtualenv is inside the project), or from the Bright Data CLI login. The JavaScript twin does not read.envat all; do not copy its wording here. - Always construct the client as
SyncBrightDataClient(auto_create_zones=False). Left on, the SDK creates Web Unlocker and SERP zones on startup, which this repository never uses, and zone creation fails without a payment method.
- Every call is an asynchronous job: trigger, poll, fetch. Profiles took 13 s to 64 s on 2026-09-18.
timeoutis seconds, 180 by default. The JavaScript twin'spollTimeoutis milliseconds. A number copied across is off by a thousand.- LinkedIn lives on
client.scrape.linkedinfor URLs and onclient.search.linkedinfor name, keyword and date searches. The JavaScript SDK has nosearch.linkedin; there, both arediscover*methods onscrape.linkedin. - The profiles dataset is
gd_l1viktl72bvl7bjuj0, the id the control panel shows. - A list of URLs is one job. The API bills per record, not per job.
This is the one to remember. With a list of URLs, _scrape_urls pairs rows with
inputs by position:
for url_item, data_item in zip(url_list, result.data):
ScrapeResult(success=True, data=data_item, url=url_item, ...)
The API returns rows in a different order on each run. Measured on 2026-09-18
with three inputs, the result labelled reidhoffman held a dead profile's error
and the result labelled with the dead slug held Reid Hoffman's profile.
- Never read
ScrapeResult.urlon list input. Match a row to its input on the row's owninput_url, orinput["url"]for an error row, which has noinput_url.attribute()insrc/linkedin_scraper/scrape.pydoes this. successis alwaysTrueon list results, error rows included. Read the row.- If fewer rows come back than inputs,
zipdrops the rest in silence. - On failure or timeout the method returns one
ScrapeResult, not a list, even for list input. - The same
zipis in the amazon, facebook, instagram, pinterest and tiktok scrapers. Tracked in sdk-python#60.
- They arrive because the executor sends
include_errors=True. The API default is off. - A dead profile:
The profile is hidden or private. - A throttled account:
Crawler error: Your system is sending too many of this type of request. If you need to send more, contact your Account Manager. Valid profiles fail with this too. Seen on 2026-09-18 after many profile calls in a short window. Wait before running again. - Match on the message, not
error_code.
search.linkedin.jobshas no parameter that caps rows, and never sendslimit_per_input. It bills one credit per posting, and a broad keyword matches thousands. The README does not run it for that reason, because every README block reruns weekly. A test pins the missing parameter, so it fails the day a cap is added.- It accepts a keyword with no location. The JavaScript twin requires
location. - Never pin a job URL in the README. A posting closes and the weekly check goes red through no fault of the code.
- Never hardcode a field list. The API also returns
timestampandinput, which the schema does not list. client.datasets.linkedin_profiles.get_metadata()returnsfieldsas a dict, but dropspii: eachDatasetFieldkeeps onlytype,active,requiredanddescription. Read the flag from the raw endpoint,https://api.brightdata.com/datasets/gd_l1viktl72bvl7bjuj0/metadata, as the field-table step inlive.ymldoes. Tracked in sdk-python#61.- The full documentation index, one
.mdpage per entry: https://docs.brightdata.com/llms.txt
The LinkedIn pipelines are linkedin_person_profile, linkedin_company_profile,
linkedin_job_listings, linkedin_posts and linkedin_people_search. There is
no linkedin_profiles. Run bdata pipelines list rather than guessing a name.
On 2026-09-16 the API went through stretches of roughly an hour where most jobs
sat until the poll deadline and returned status: "timeout" with no rows.
Between those stretches the same calls finished in about a minute. Load did not
explain it. A red live check whose only symptom is timeouts is probably a bad
wave: rerun it before looking for a code change.
pytestruns offline and needs no token.ruff check .must pass.- CI installs from the README's own commands on an empty machine. A weekly workflow executes every fenced block in the README against the real API, and a daily one runs a smaller live check.
- The field table sits between
<!-- fields:start -->and<!-- fields:end -->and is regenerated daily. Do not edit it by hand. - The "last verified" badge line is rewritten by the daily run. Do not edit it.
- Keep it small: 15 files and about 300 lines of Python. Do not add retries, deduplication, scheduling, databases, async examples or concurrency.