Troubleshooting
The handful of things that trip people up, and how to fix each one.
Most of these come down to network reality, not a bug. LinkedIn is a public website with a sign-in wall, and linkedin is honest about what it can and cannot read.
"blocked" and exit code 4
LinkedIn serves some surfaces to anonymous visitors and walls the rest behind a
sign-in wall. When the page linkedin asks for is walled, it exits with code 4
("auth required") rather than returning the wall as if it were data. The wall
shows up two ways: HTTP 999 is LinkedIn's bot block, and an authwall is a
redirect to /authwall, /uas/login, /login, /checkpoint, or /signup.
What is walled, plainly:
- School pages (
/school/<slug>) return the bot block (HTTP 999). There is noschoolfetch command;idonly classifies a school URL. - The activity and
/posts/subpages of profiles and companies redirect to/uas/login. That is why posts come from the JSON-LD graph on the main page, not those subpages. - People and company search require sign-in.
What works anonymously: profile, company, job, and jobs search. Profile
and company pages return 200 and read reliably. post is best effort but
generally returns data for single public posts and articles.
About HTTP 999
999 is LinkedIn's bot block. linkedin sends no Referer header, because a
same-site referer is one signal LinkedIn reads as scraping and answers with 999.
With no referer, profile and company pages return 200, so 999 now only shows up
on the genuinely walled surfaces above (school pages most of all). If you hit a
999 on a surface that normally works, it is most likely IP-level rate-limiting,
not the page being walled. Slow down with --rate, or lend a session with
--cookies.
What to do, in order:
-
Use the surfaces that work anonymously.
jobsandjobuse the guest endpoints,companyreads the Organization JSON-LD plus the about panel, andprofilereads the Person JSON-LD. Prefer them for the fields they carry. -
Slow down and retry. The default
--rateis already two seconds. Raise it if you are seeing 999 on normally-working pages; the same page can succeed a moment later at a gentler rate. -
Lend a session with
--cookies. Export a Netscapecookies.txtjar from a signed-in browser and pass it:linkedin profile williamhgates --cookies ~/cookies.txtA real session usually clears the wall.
The cookies.txt format
--cookies expects a Netscape cookie jar: the plain-text format most browser
extensions export and curl reads. Each line is tab-separated:
www.linkedin.com FALSE / TRUE 0 li_at abc123...
Lines starting with # are comments. Export it from a browser where you are
signed in to LinkedIn, save it somewhere private, and pass its path to
--cookies. linkedin only replays the jar; it never logs in for you and never
stores credentials.
"no results" and exit code 3
Exit code 3 means linkedin reached the page but found nothing to return: a job
search with no matches, an empty result. Check the slug, id, or URL is right
(use linkedin id <input> to see how linkedin classifies it), broaden a job
search, or loosen the filters. A genuine 404 or an unknown id exits with code 6
("not found") instead.
Rate limiting (429)
If LinkedIn returns 429 (too many requests), linkedin backs off and retries up
to --retries times. When the retries are exhausted the command exits 5 ("rate
limited"). If you see this often, you are going too fast: raise --rate and let
the cache absorb repeat fetches. The default (a two second spacing) is set to
avoid this.
Partial failures in a multi-fetch
When you pass several inputs at once (profile a b c, job 1 2 3, or jobs --hydrate) and some inputs fail (often a walled post in the batch, or one input
that got rate-limited), linkedin prints the records that did parse and exits 0;
each failure is logged as a warning on stderr. Re-run the failed ones later, slow
down with --rate, or pass --cookies for the walled ones.
If every input fails, the command exits with the failure's own code: 4 for a walled page, 5 for a rate-limited one, or 6 for a not-found one. If nothing matched at all, it exits 3.
Where state lives
The on-disk cache and the SQLite store both live under the data dir (the XDG
data directory by default, or LINKEDIN_DATA_DIR / --data-dir). The store file
is fixed at <data-dir>/linkedin.db, so to move it, point --data-dir at
another directory. To see the resolved paths:
linkedin info
To clear the cache and start fresh:
linkedin cache clear