---
id: duplicate-jobs
title: Duplicate job detection
description: How JobOps deduplicates cross-source postings and hides repeats after you skip or apply.
sidebar_position: 8
---

## What it is

JobOps treats the same role reposted on different boards as **one opportunity**, and remembers when you have already **skipped** or **applied** so you do not see it again.

Duplicate detection uses normalized keys:

- **Employer + title** — strips punctuation, `(Remote)`, trailing city lines, and legal suffixes (`Inc.`, `Ltd.`) so `Acme Inc.` / `SDET (Remote)` matches `Acme` / `SDET`.
- **Employer + description** — when the job description is long enough, the same posting copy under the same company matches even if the title wording differs slightly.

This runs at **import time** (pipeline) and in the **Jobs list** (UI).

## Why it exists

Job boards repost the same role on LinkedIn, Indeed, QAJobsBoard, and aggregators. Skipping or applying once should mean you do not wade through the same listing again on the next run.

## How to use it

You do not configure duplicate detection separately. It is always on for your profile.

### When you skip a job

1. Skip from the job detail panel or press **`s`** on the Jobs page.
2. JobOps marks that row `skipped`.
3. Any other **Discovered** or **Ready** jobs with the same employer+title (or same employer+description) are **auto-skipped**.
4. Future pipeline imports that match those keys are **not imported**.

### When you mark applied

1. Mark the job applied from the UI.
2. Duplicate open rows are **auto-skipped** (not marked applied) so your Applied tab stays clean.
3. Future imports of the same role are suppressed the same way as for skips.

### Cross-source import

During a pipeline run, if a new posting matches an existing row by URL, source id, or content fingerprint, JobOps **does not create a second row** — the import is counted as skipped in run stats.

### Jobs list

Open jobs that match a prior skip or apply are **hidden** from Discovered, Ready, and All tabs so the queue stays fresh. Skipped and applied rows themselves remain visible in their statuses.

Use **Filters → Employer keywords → Hide roles you already skipped** to toggle this behavior (on by default). When enabled, JobOps compares **company + job title** only:

- Same company after normalization (legal suffixes stripped; short names like `CGI` match longer forms like `CGI IT UK Limited`).
- Same title after normalization (including simple plural variants such as `Engineer` vs `Engineers`).
- Prior skip or apply must be within the **last 90 days** (`updatedAt` on the skipped/applied row).

Uncheck the filter to temporarily review rediscovered rows that match older skips.

### Clean up rediscovered rows already in Discovered

If older pipeline runs left Discovered copies of roles you already skipped or applied:

1. Open **Settings → Danger Zone**.
2. Use **Clear Rediscovered Archive Matches**.
3. Confirm. JobOps permanently deletes matching **Discovered** rows only (Ready / Applied / Skipped stay).

Matching uses the same employer+title (and employer+description) keys as skip/import dedup.

## Defaults and constraints

- Prior-skip hiding uses employer + title only (not description text).
- The 90-day window applies to both the Jobs list filter and pipeline import suppression for skipped/applied rows.
- Description matching during import still requires at least **80 characters** of normalized text; short or empty descriptions fall back to employer+title only.
- Matching is **per profile** (`ownerProfileId`); different login profiles do not share dedup state.
- Dedup does **not** delete existing rows retroactively when you change skip list or country filters — use **Clear Rediscovered Archive Matches** in Danger Zone, run discovery again, or skip manually for old data.
- Very different titles at the same company (for example `SDET` vs `Product Designer`) are **not** collapsed.

## Common problems

### I still see the same job from another source

- Titles or employer names may differ enough that normalization does not match (for example a staffing agency name vs the hiring company).
- Add the employer to the [Company skip list](./company-skip-list) if it is always noise.
- Skip one row — siblings with matching keys should auto-skip and future imports should stop.

### A different role at the same company disappeared

- Employer+title dedup only merges **the same normalized title**. Different roles at one company should remain separate.
- If two titles normalize to the same string, check for overly generic titles on the board.

### Skipped jobs reappear after a pipeline run

- Confirm the skip saved (status `skipped` in the UI).
- If the repost uses a new employer spelling and a new title **and** a short description, it may not match — block the employer or country instead.

## Related pages

- [Orchestrator](/docs/next/features/orchestrator)
- [Pipeline Run](/docs/next/features/pipeline-run)
- [Company skip list](./company-skip-list)
- [Blocked countries](./blocked-countries)
- [Keyboard Shortcuts](/docs/next/features/keyboard-shortcuts)
