# LinkedIn Search vs FreeHire Search: Key Differences for Job Discovery in the AI Job Search Toolkit

> Discover the key differences between LinkedIn Search and FreeHire Search for AI-powered job discovery. Learn which tool best suits your needs.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: comparison
- Published: 2026-08-05

---

**Both `linkedin-search` and `freehire-search` are country-agnostic portal skills that follow the same CLI contract, but they differ fundamentally in data source, query model, and output granularity.**

The `ai-job-search` repository by MadsLorentzen provides two distinct job discovery tools under `.agents/skills/`: `linkedin-search` and `freehire-search`. While both expose identical `search` and `detail` commands, analyzing the source code reveals they serve different technical architectures and use-cases. This guide breaks down how each works, when to choose one over the other, and how they integrate into automated workflows.

## Core Architecture: HTML Scraping vs JSON API

The most significant difference lies in how each skill retrieves job data.

### LinkedIn Search: HTML Scraping Pattern

`linkedin-search` operates as a **web scraper**. It fetches LinkedIn's public `jobs-guest` HTML pages and parses them on the fly. In [`cli/src/helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/src/helpers.ts), the `parseJobCards` function uses regex extraction to split the raw HTML into job card chunks and extract fields like title, company, and location.

This approach requires no API key but is inherently fragile—LinkedIn's page structure can change without notice. The skill implements exponential back-off for 429/5xx responses, as noted in its [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md).

### FreeHire Search: REST API Pattern

`freehire-search` consumes a **structured JSON API** from freehire.me. The backend aggregates approximately 50 ATS platforms into a normalized schema. The CLI builds query strings from facet flags and calls `/api/v1/agent/jobs/search`, forwarding the JSON response directly with minimal post-processing.

Unlike LinkedIn's guest pages, this API is explicitly public and designed for programmatic access. The skill also supports self-hosting via the `FREEHIRE_API_URL` environment variable, pointing to the MIT-licensed `strelov1/freehire` project.

## Query Models: Free-Text Location vs Controlled Facets

How you specify search parameters differs substantially between the two tools.

### LinkedIn: Simple Location Strings

`linkedin-search` accepts a free-text `--location` (or `-l`) flag that passes directly to LinkedIn's search. This makes it genuinely global—any market LinkedIn serves works with the same CLI.

```bash
bun run .agents/skills/linkedin-search/cli/src/cli.ts \
  search -q "product manager" -l "Berlin, Germany" --remote remote --format table

```

### FreeHire: Structured Facet Flags

`freehire-search` uses controlled vocabulary facets: `--region`, `--country`, `--city`, `--remote`, `--seniority`, and more. Location is not free-text but a filtered index matching freehire's dictionaries.

```bash
bun run .agents/skills/freehire-search/cli/src/cli.ts \
  search -q "backend engineer" --seniority senior --country DE --jobage 14 --format table

```

This design trade-off favors precision for technical roles over geographic flexibility.

## Output Granularity: Preview vs Full Description

The information returned by `search` commands varies dramatically.

| Aspect | `linkedin-search` | `freehire-search` |
|--------|------------------|-------------------|
| `search` output | Title, company, location, ID **only** (preview) | **Full description** included for every hit |
| `detail` purpose | Required to fetch complete job posting | Re-fetch by slug; rarely needed after search |
| Payload size | Minimal | Larger, but complete |

For `linkedin-search`, you must make a second request:

```bash
bun run .agents/skills/linkedin-search/cli/src/cli.ts \
  detail 4426311357 --format plain

```

For `freehire-search`, `detail` is mainly for re-retrieval by slug:

```bash
bun run .agents/skills/freehire-search/cli/src/cli.ts \
  detail golang-zensar-2bxu6dxm --format plain

```

## Scope and Target Use-Cases

### LinkedIn: General-Purpose, Any Industry

`linkedin-search` covers all industries and seniority levels. Use it when you need:
- LinkedIn-specific fields (seniority level, employment type, direct apply links)
- Ad-hoc searches across arbitrary geographies
- Familiar job board coverage without domain restrictions

**Warning:** The CLI displays a prominent `⚠️ Personal use only` notice. Automated scraping violates LinkedIn's Terms of Service.

### FreeHire: Tech-Focused, Structured Workflows

`freehire-search` is optimized for software engineering, data science, and DevOps roles. Its facet dictionaries align with technical job classifications. While non-technical listings exist, coverage is less reliable.

Best suited for:
- Programmatic pipelines filtering by seniority, remote status, and tech stack
- Multi-market technical searches with consistent output structure
- Self-hosted deployments requiring data sovereignty

## Shared Infrastructure: Zero-Dependency Skills

Both tools are built as isolated skills under `.agents/skills/` and declare `allowed-tools: Bash(bun run …)` in their respective [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) files. This means:

- **Zero runtime dependencies** beyond `bun` itself
- Automatic discovery by the higher-level `/scrape` workflow (see [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md))
- Identical CLI contract: `search` for listings, `detail` for single postings, `--format` for output control

The framework treats them as portable binaries runnable in any fork without installation steps, as documented in [`SETUP.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SETUP.md).

## Self-Hosting and Portability

Only `freehire-search` offers true backend portability. The `FREEHIRE_API_URL` environment variable allows pointing to private instances of the `strelov1/freehire` aggregator. This makes it viable for organizations requiring:

- Internal job board aggregation
- Compliance with data residency requirements
- Custom ATS integrations beyond the public freehire.me instance

`linkedin-search` has no self-hosting equivalent—it always communicates with LinkedIn's live infrastructure.

## Summary

- **Data source:** `linkedin-search` scrapes HTML; `freehire-search` calls a REST JSON API
- **Query style:** LinkedIn uses free-text locations; FreeHire uses controlled facet flags
- **Result completeness:** LinkedIn requires `detail` calls for full descriptions; FreeHire returns them in `search`
- **Domain focus:** LinkedIn is general-purpose; FreeHire targets technical roles
- **Self-hosting:** Only FreeHire supports private backend instances via `FREEHIRE_API_URL`
- **Terms of Service:** Both are personal-use-only, but LinkedIn scraping carries higher enforcement risk

## Frequently Asked Questions

### Can I use both skills in the same automated workflow?

Yes. The `/scrape` workflow in `ai-job-search` auto-discovers all skills under `.agents/skills/` that match the `search/detail` CLI contract. You can query both LinkedIn and freehire.me in sequence, though you should respect rate limits and TOS restrictions for each source.

### Why does `linkedin-search` need a separate `detail` command?

LinkedIn's guest pages only show preview information in search results. The full job description requires a separate page fetch. In [`cli/src/helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/src/helpers.ts), the HTML parsing logic extracts different fields for `search` versus `detail` commands. This two-step pattern keeps initial queries lightweight but adds latency for complete data.

### Is `freehire-search` limited to specific countries?

No—both skills are country-agnostic. However, `freehire-search` uses ISO country codes and controlled region vocabularies rather than free-text locations. The underlying aggregator indexes ~50 ATS platforms globally, though coverage density varies by market.

### Which skill is better for high-volume automated scraping?

Neither is ideal for unsupervised high-volume use. Both include rate-limit handling with exponential back-off, but LinkedIn actively blocks scrapers and freehire.me operates as a best-effort service without SLA. For production pipelines, consider self-hosting the `strelov1/freehire` backend to eliminate external dependency risks.