---
name: job-search
description: >-
  Search 120K+ jobs in Germany by skills, location, salary, and remote
  preference. Match CVs against job requirements with scoring and gap analysis.
  Use when the user wants to find jobs, match their CV, or save interest signals.
homepage: https://www.talent.de
license: Free-to-use
compatibility: Requires HTTP client and network access.
metadata:
  openclaw:
    emoji: "\U0001F50D"
  talent:
    category: job-intent
    version: "5.2.2"
    api_base: https://www.talent.de/api
---

# Job Search — Find & Match Jobs via API

Search jobs in Germany by keywords, location, skills, salary, and remote preference. Match CVs against job requirements to get scores, gap analysis, and surplus skills. Open access for search, Access-ID required for matching.

## Agent Guidelines

> **Present results before acting.** Always show search results to the requestor before matching or saving interest. Let them choose which jobs to explore further.

> **No assumptions.** Only search for what the user explicitly asks. Don't add filters they didn't mention.

> **Summarize, don't dump.** Present the top 3-5 results as a brief list (title, company, location, key skills). Offer to show more or refine the search.

> **Match requires a CV.** If the user wants to match but has no CV, guide them to the [CV Builder](https://www.talent.de/skills/cv-builder/SKILL.md) skill first.

## User Communication

| Step | Say to the user |
|------|--------------------|
| Before search | "Let me search for jobs matching your criteria." |
| Results found | "I found {total} jobs. Here are the top matches:" |
| No results | "No jobs match those exact criteria. Want me to try a broader search?" |
| Before matching | "I'll compare your CV against these jobs to show how well you fit." |
| Match results | "Here's how you match: {score}% for {title}. You cover {n} skills, with {gaps} gaps." |
| Save interest | "I've saved your interest in {title}. The employer can see an anonymous signal." |

## Quick Start

1. Search: `GET /api/agent/jobs?q=react&location=Berlin`
2. Present results, let user pick jobs of interest
3. Match: `POST /api/agent/jobs/match` with `cv_id` + selected `job_ids`
4. Present match scores, gaps, and surplus skills
5. Save interest: `POST /api/agent/jobs/interest` with `job_id`

## Search Jobs

```http
GET https://www.talent.de/api/agent/jobs?q=react&location=Berlin&remote_min=50&limit=10
```

Open access (IP rate-limited). With Access-ID: 100 searches/day.

### Query Parameters

| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `q` | string | — | Keywords, job title, or skill names |
| `query` | string | — | Alias of `q` (same behavior) |
| `location` | string | — | City name or German postal code (e.g., "Berlin", "10115") |
| `radius` | number | 50 | Search radius in km (1-500, used with location) |
| `remote_min` | number | — | Minimum remote percentage (0-100) |
| `salary_min` | number | — | Minimum annual salary in EUR |
| `skills` | string | — | Comma-separated skill names (e.g., "react,typescript") |
| `employment_type` | string | — | FULL_TIME, PART_TIME, CONTRACT, FREELANCE, INTERNSHIP |
| `experience_level` | string | — | ENTRY, JUNIOR, MID, SENIOR, LEAD, EXECUTIVE |
| `sort` | string | relevance | `relevance`, `date`, or `salary` |
| `limit` | number | 20 | Results per page (1-50) |
| `offset` | number | 0 | Pagination offset |
| `wdl_only` | boolean | true | Optional alias (`false` includes non-WDL jobs) |
| `hasApplicationEmail` | boolean | auto (`true` when `wdl_only=true`) | Only jobs with extracted application email; required for anonymous apply flow and CV-by-email send |
| `anonymous_only` | boolean | — | Agent alias for `hasApplicationEmail` |

### Example Response

```json
{
  "success": true,
  "jobs": [
    {
      "id": "a1b2c3d4-...",
      "title": "Senior React Developer",
      "company_name": "SAP SE",
      "location": "Berlin",
      "remote_pct": 80,
      "salary": { "min": 70000, "max": 90000, "currency": "EUR" },
      "employment_type": "FULL_TIME",
      "experience_level": "SENIOR",
      "posted_at": "2026-02-20T10:00:00Z",
      "skills": [
        { "name": "React", "required": true },
        { "name": "TypeScript", "required": true },
        { "name": "Node.js", "required": false }
      ],
      "languages": [{ "name": "English", "level": "C1" }],
      "wdl_enabled": true,
      "has_application_email": true,
      "application_url": "mailto:jobs@example.com",
      "match_score": 85,
      "description_excerpt": "We are looking for an experienced React developer..."
    }
  ],
  "total": 342,
  "limit": 20,
  "offset": 0
}
```

`description_excerpt` contains the first 300 characters. `match_score` (1-100) is present when sorted by relevance.
`wdl_enabled` is actionable only for jobs with `has_application_email=true`.

## Match CV to Jobs

Requires Access-ID. Compares a CV's skills, tools, languages, and certificates against job requirements.

```http
POST https://www.talent.de/api/agent/jobs/match
x-access-id: talent_agent_xx42
Content-Type: application/json

{
  "cv_id": "cv_abc123",
  "job_ids": ["job_1", "job_2", "job_3"]
}
```

Max 10 jobs per request. The CV must belong to the same Access-ID.

### Match Result

Each job returns:

| Field | Description |
|-------|-------------|
| `overallScore` | 0-100, weighted match percentage |
| `autoMatched` | Skills the CV already covers |
| `deltas` | Gaps — requirements the CV doesn't cover |
| `surplus` | Extra skills the CV offers beyond requirements |

```json
{
  "success": true,
  "matches": [{
    "job_id": "job_1",
    "overallScore": 82,
    "autoMatched": [
      {"key": "skill.react", "level": "Expert"},
      {"key": "skill.typescript", "level": "Expert"},
      {"key": "language.english", "level": "C2"}
    ],
    "deltas": [
      {"key": "tool.kubernetes", "state": "GAP", "category": "Tool"},
      {"key": "tool.graphql", "state": "GAP", "category": "Tool"}
    ],
    "surplus": [
      {"key": "tool.aws", "level": "Expert"},
      {"key": "tool.docker", "level": "Expert"}
    ]
  }]
}
```

**Present to user:** "You're an 82% match. You cover React, TypeScript, and English. Two gaps: Kubernetes and GraphQL. Plus you bring AWS and Docker as bonus skills."

## Save Interest

Record anonymous interest in a job. The employer sees a signal count, not the candidate's identity.

```http
POST https://www.talent.de/api/agent/jobs/interest
x-access-id: talent_agent_xx42
Content-Type: application/json

{"job_id": "job_1"}
```

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/agent/jobs/interest` | Save interest in a job |
| GET | `/api/agent/jobs/interest` | List all saved interests |
| DELETE | `/api/agent/jobs/interest` | Remove interest |

Requires Access-ID.

## Agent Dialog Flow

```
User:  "Find me React jobs in Berlin with 80% remote"
Agent: GET /api/agent/jobs?q=react&location=Berlin&remote_min=80
       → "I found 47 jobs. Here are the top 5:"
       1. Senior React Developer — SAP SE (Berlin, 80% remote)
       2. Frontend Engineer — Zalando (Berlin, 100% remote)
       3. ...

User:  "Match #1 and #3 against my CV"
Agent: POST /api/agent/jobs/match { cv_id, job_ids: [id1, id3] }
       → "Job #1: 82% match. 5 skills covered, 2 gaps (K8s, GraphQL)."
       → "Job #3: 91% match. 7 skills covered, no gaps."

User:  "I'm interested in #3"
Agent: POST /api/agent/jobs/interest { job_id: id3 }
       → "Saved! The employer can see anonymous interest."
```

## Edge Cases

| Situation | What to do |
|-----------|------------|
| No results | Suggest broader search: remove location, lower remote_min, fewer skills |
| No CV for matching | "You need a CV first. Want me to help create one?" → [CV Builder](https://www.talent.de/skills/cv-builder/SKILL.md) |
| Rate limit hit | "Daily search limit reached. Resets at midnight UTC." |
| Too many results | Add filters: skills, experience_level, remote_min |

## Generative UI Dashboards (streaming)

Visual match reports rendered client-side from streaming JSONL.

```http
POST https://www.talent.de/api/agent/ui/job-match
Content-Type: application/json

{
  "prompt": "Show match report for these jobs",
  "match_data": [
    {
      "jobTitle": "Senior React Developer",
      "company": "SAP SE",
      "overallScore": 85,
      "autoMatched": [{"name": "React"}, {"name": "TypeScript"}],
      "deltas": [{"requirement": "Kubernetes", "bridgeType": "learning"}],
      "surplus": [{"name": "Go", "category": "hard"}]
    }
  ]
}
```

Returns streaming JSONL. Use `useUIStream()` from `@json-render/react` to render.

## Guardrails

- Rate limits (with Access-ID): 100 searches/day, 50 matches/day
- Rate limits (without Access-ID): 10 searches/day per IP
- Max 10 jobs per match request
- Max 50 results per search page
- Always present match results before proceeding to negotiation

## References

- [Job Data Reference](https://www.talent.de/skills/job-search/reference/jobs.md): Full query params, response fields, pagination
- [Access System](https://www.talent.de/skills/shared/access.md): Rate limits and Access-ID registration
- [Error Codes](https://www.talent.de/skills/shared/errors.md): Error reference and troubleshooting
- [Privacy](https://www.talent.de/skills/shared/privacy.md): Data handling and GDPR compliance

## Next Steps

After matching, candidates can negotiate conditions via WDL:
- [Job Negotiate](https://www.talent.de/skills/job-negotiate/SKILL.md): Conditional apply, counter-offers, deals (coming soon)
