# How the LinkedIn Search Integration Works in the AI Job Search Framework

> Discover how the AI Job Search framework integrates with LinkedIn search. It fetches live job listings via a zero-dependency CLI, parsing HTML into markdown for AI processing.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-09-01

---

**The AI Job Search framework fetches live LinkedIn job listings through a zero-dependency CLI skill that queries public `jobs-guest` endpoints without authentication, parsing HTML responses into structured markdown for downstream AI processing.**

The LinkedIn search integration is a core component of the [MadsLorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search) repository, enabling autonomous job discovery without API keys or browser automation. This self-contained skill resides under `.agents/skills/linkedin-search` and interfaces directly with the framework's generic job-scraper orchestration layer.

## Architectural Components

The integration follows a modular, skill-based architecture designed for minimal footprint and maximum portability.

### Skill Registry and Descriptor

The framework discovers the LinkedIn capability through [`.agents/skills/linkedin-search/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/SKILL.md), which declares the skill's interface, supported flags, and execution context to the AI agent. This descriptor allows the framework to route user requests for LinkedIn listings to the appropriate handler without hard-coded dependencies.

### CLI Entrypoint and Validation

The primary execution logic lives in [`.agents/skills/linkedin-search/cli/src/cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/src/cli.ts). This TypeScript entrypoint parses command-line arguments, validates the required `--location` parameter via `helpers.validateLocation`, and orchestrates the HTTP request lifecycle. The CLI is designed as a standalone Node.js package with zero runtime dependencies beyond the standard library and `node-html-parser`, enabling installation and execution across JavaScript runtimes like Node.js or Bun.

### Helper Utilities and URL Construction

Low-level networking and data extraction logic is encapsulated in [`.agents/skills/linkedin-search/cli/src/helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/src/helpers.ts). This module constructs requests to LinkedIn's public `https://www.linkedin.com/jobs-guest/search` endpoint, implements the `convertJobAgeToSeconds` function to map human-readable age filters (e.g., "last 120 minutes") to LinkedIn's internal `f_TPR` query parameter, and parses HTML responses to extract job IDs, titles, companies, locations, descriptions, and posting dates.

### Integration with Job Scraper

The generic job-scraper skill located at [`.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/SKILL.md) registers `linkedin.com/jobs` as a supported source. When users request LinkedIn-specific searches through the framework's `/search` command, the orchestrator delegates to the LinkedIn Search CLI, streams the resulting markdown table or JSON back into the conversational context, and passes structured job metadata to subsequent pipeline stages like `/rank` and `/apply`.

## Data Flow and Execution Pipeline

The LinkedIn search skill operates through a deterministic five-stage pipeline:

1. **Invocation** – The user submits a search query via the framework's `/search` command or directly invokes the CLI with the `-l` (location) flag, such as `-l "Berlin, Germany"`.

2. **Request Construction** – The CLI builds a URL targeting LinkedIn's `jobs-guest` endpoint, appending query parameters for location and, if specified, time-based filters using the `f_TPR` parameter derived from `--jobage-minutes` values.

3. **HTTP Fetching** – The system issues an unauthenticated GET request against the public endpoint. Exponential back-off retry logic handles HTTP 429 (rate limit) and 5xx server errors to respect LinkedIn's infrastructure.

4. **HTML Parsing** – Using `node-html-parser`, the [`helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/helpers.ts) module traverses the returned HTML document, locates job card elements, and extracts structured fields including job ID, title, company name, location, description snippet, and relative posting age.

5. **Output Formatting** – Extracted data is serialized into either markdown tables (default) or JSON arrays based on the `--output` flag, then ingested by the framework's AI pipeline for candidate-profile matching and application preparation.

## Key Implementation Files

Understanding the source structure requires familiarity with these specific file paths:

- **[`.agents/skills/linkedin-search/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/SKILL.md)** – Declares the skill interface to the framework's skill registry, exposing supported flags and integration metadata.

- **[`.agents/skills/linkedin-search/cli/src/cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/src/cli.ts)** – Contains the main execution loop, argument parsing logic, and high-level error handling for the CLI tool.

- **[`.agents/skills/linkedin-search/cli/src/helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/src/helpers.ts)** – Implements `convertJobAgeToSeconds` for LinkedIn time-parameter conversion and HTML extraction routines for job card parsing.

- **[`.agents/skills/linkedin-search/cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/package.json)** – Defines the package manifest specifying zero runtime dependencies, ensuring the CLI can be installed and executed in isolated environments without credential management overhead.

- **[`.agents/skills/linkedin-search/cli/README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/README.md)** – Provides user-facing documentation and displays a legal disclaimer restricting usage to personal, low-volume research only.

- **[`.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/SKILL.md)** – Serves as the integration bridge, listing LinkedIn as a supported upstream source and defining the delegation contract between the generic scraper and the LinkedIn-specific skill.

## Usage Examples

Execute a basic search for jobs in Berlin with default markdown output:

```bash
linkedin-cli -l "Berlin, Germany"

```

Retrieve recent remote positions posted within the last 120 minutes as JSON:

```bash
linkedin-cli -l "Remote" --output json --jobage-minutes 120

```

Programmatic invocation within the framework's TypeScript codebase:

```typescript
import { runLinkedInSearch } from "./.agents/skills/linkedin-search/cli/src/cli";

const results = await runLinkedInSearch({
  location: "London, United Kingdom",
  output: "markdown",
  jobAgeMinutes: 0
});

// Returns markdown table for AI pipeline consumption
console.log(results);

```

## Summary

- The LinkedIn search skill queries public `jobs-guest` endpoints requiring no authentication or API keys.
- Core logic resides in [`.agents/skills/linkedin-search/cli/src/cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/cli/src/cli.ts) with helper utilities in [`helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/helpers.ts).
- The integration supports filtering by location and posting age through the `--jobage-minutes` flag mapped to LinkedIn's `f_TPR` parameter.
- Zero runtime dependencies ensure portable execution across JavaScript runtimes.
- The skill connects to the broader framework via [`.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/SKILL.md) for seamless pipeline integration.
- Built-in exponential back-off handles rate limiting, while documentation enforces personal-use-only restrictions.

## Frequently Asked Questions

### Does the LinkedIn search integration require API credentials or a LinkedIn account?

No. The skill queries LinkedIn's public `jobs-guest` endpoints, which return HTML job listings accessible without authentication, cookies, or API keys. This design deliberately avoids credential management while respecting LinkedIn's Terms of Service for personal, low-volume usage.

### How does the framework handle LinkedIn's anti-scraping measures and rate limits?

The CLI implements exponential back-off retry logic for HTTP 429 (Too Many Requests) and 5xx server errors. Additionally, the [`README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/README.md) and CLI banner enforce a "personal use only" policy, discouraging automated bulk scraping that would trigger aggressive rate limiting or IP blocking.

### What data fields are extracted from LinkedIn job postings?

The [`helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/helpers.ts) parsing logic extracts job ID, title, company name, location, description snippet, and posting date/age from LinkedIn's HTML job cards. These fields are normalized into markdown tables or JSON structures for downstream AI processing in the ranking and application stages.

### Can I filter results by how recently jobs were posted?

Yes. The `--jobage-minutes` flag accepts an integer value that `convertJobAgeToSeconds` transforms into LinkedIn's internal `f_TPR` query parameter. For example, passing `--jobage-minutes 120` restricts results to positions posted within the last two hours.