

# How to Get New Job Alerts Without Repeated Listings

To create new notice work for a new nonblank platform plus job ID once and handle existing eligible pending or failed retries, store each collected public listing in a persistent ledger.

A collection run gathers collected public listings, but a date filter proves no active opening. Different listing IDs can also point to the same underlying vacancy. This method gives no duplicate prevention or exactly-once guarantee. Unknown delivery needs review before retry.

```text
New Collection Batch
   |
   +---> New Nonblank Platform + Job ID? ---> Create Pending Notice Work (Once)
   |
   +---> Existing Eligible Pending/Failed ID? ---> Retry Notice Work
```

How to Get New Job Alerts Without Repeated Listings

## Essential Terms

Understanding these basic terms helps keep your job tracking clean and accurate:

- Listing: One public job advertisement published on a job board, identified by a text string called a job ID.
- Actor: A hosted collection program that fetches job data from public web pages.
- Task: A saved set of search inputs, such as target keywords and location, used by an Actor.
- First-seen time: The exact time your tracking system first records a specific listing ID. This is different from the original publication time on the job board.
- Alert state: A record showing whether a notice for a job is pending, successfully sent, or failed.

## Setting Up Your Source Collection

Before tracking new postings, you need a stable daily job feed. You can use the [LinkedIn Jobs Scraper](https://apify.com/chronometrica/linkedin-jobs-scraper) to collect raw job listings.

Start with a narrow search preset so you can test your tracking ledger easily. The default task uses several keywords, but you should change the input to target a single keyword first.

```json
{
  "searchTerm": "software engineer",
  "location": "United States",
  "maxItems": 3,
  "postedWithin": "7d", "distance": 25,
  "sortBy": "date",
  "jobType": "any",
  "workplaceType": "any",
  "experienceLevel": "any",
  "maxPagesPerSearch": 5,
  "saveOnlyUniqueItems": true,
  "balanceKeywordCoverage": true,
  "fetchJobDetails": true,
  "requestDelayMillis": 500
}
```

Setting Up Your Source Collection

Setting `saveOnlyUniqueItems` to `true` removes duplicate listings within a single run. However, it does not stop repeats across different days. The `maxItems` limit applies across all search terms in one run. If a run hits a numerical limit or meets a source failure, some jobs might be missed. Treat missed or failed collections as unknown data rather than assuming zero new jobs were posted.

When a run succeeds, extract these key fields for your tracking pipeline:

The collector outputs specific fields for each listing.

| Field Name | Description |
| --- | --- |
| `jobId` | Source platform job identifier |
| `jobUrl` | Source platform job link |
| `title` | Role title |
| `companyName` | Hiring company name |
| `locationRaw` | Raw location text |
| `postedAt` | Derived or parsed publication time |
| `postedAtRaw` | Raw publication label from source |
| `scrapedAt` | Time of collection |
| `descriptionStatus` | Status of job description fetch |

Raw labels stay with parsed or derived times. Relative age is not an exact original publication time. Reader first-seen, last-seen, and action fields are separate. The known three-row current acceptance serves as source identity and time shape only, not deployed multiday alert proof.

## The 8-Step Daily Tracking Method

Follow these eight steps to process incoming listings and manage alerts without sending duplicates.

1. Define an eligible alert: An eligible alert is a collected listing ID that meets your basic criteria (such as role title or location) and has not been processed before. Keep your filter rules next to your run records.
2. Preserve source rows and inspect run output: Save raw batch outputs. Check for collection errors, item caps, or warnings before processing rows. A failed batch means market conditions are unknown, not that there are zero new listings.
3. Create a tracking table keyed by nonblank platform and jobId as text. Missing IDs stay separate unresolved observations and never merge blanks. Keep reader fields `first_seen_at`, `last_seen_at`, `jobUrl`, `review_status`, `alert_state`, `alert_attempt_at`, and `error_note`. Source collection `scrapedAt` differs from workflow action time.
4. Process incoming batch IDs: For every unique key in the current batch, check if it exists in your tracking table. If it exists, update `last_seen_at` only. If it is missing, create a new row with `review_status` set to pending and `alert_state` set to pending. Never overwrite `first_seen_at`.
5. Review eligibility before sending: Verify required conditions such as [location or remote work status](/en/articles/jobs-remote-eligibility/). If a required condition cannot be verified, keep `review_status` as pending. If a job fails your rules, mark it as excluded with a clear reason so future runs do not review it again.
6. Give the notice a stable key tied to platform plus job ID. Send only eligible pending notices. Mark sent only after the provider service confirms its action. Sent means confirmed provider submission, not proof of inbox arrival. HTTP 200 acceptance alone is not delivery confirmation. Unknown confirmation means delivery is unknown and must be inspected before retry. There is no exactly-once delivery guarantee.
7. Handle delivery failures safely: If a notice fails to send, retry delivery later without clearing `first_seen_at` or re-collecting jobs from the source. Keep collection errors separate from delivery errors. Save your state after every step so you can restart safely.
8. Report metrics clearly: Separate your metrics into collected unique listings, newly observed IDs, eligible notices, confirmed sent notices, and pending or failed notices. Having no new notices means no new accepted matches were found today, not that the overall job market has stopped hiring.

## Fictional Worked Example

All IDs, dates, and events are fictional. The baseline before October 3 contains only two keys: `J1` first seen Oct 1 sent, and `J2` first seen Oct 2 sent. The final ledger contains four keys, not the baseline.

On October 3, collection returns four raw/import rows containing keys `J1`, `J2`, `J3`, and `J3`. This represents 4 observations, 3 distinct keys, 2 existing keys, and 1 unseen key (`J3`). `J3` review passes. Its first notice attempt is confirmed not accepted, so a later retry is safe in this fictional case.

On October 4, collection returns 3 keys: `J2`, `J3`, and `J4`. This represents 3 distinct keys, 2 existing keys, and 1 unseen key (`J4`). `J4` missing location is review pending with no notice. One provider-confirmed submission on Oct 4 is not inbox delivery.

On October 5, a failed collection changes no history and gives no absence claim.

Master Tracking Table:

| Key | First Seen | Last Seen | Review Status | Alert State | Attempt Time | Error Note |
| --- | --- | --- | --- | --- | --- | --- |
| linkedin:J1 | 2026-10-01T08:00:00Z | 2026-10-03T08:00:00Z | Passed | Sent | 2026-10-01T08:05:00Z | None |
| linkedin:J2 | 2026-10-02T08:00:00Z | 2026-10-04T08:00:00Z | Passed | Sent | 2026-10-02T08:05:00Z | None |
| linkedin:J3 | 2026-10-03T08:00:00Z | 2026-10-04T08:00:00Z | Passed | Sent | 2026-10-04T08:05:00Z | Oct 4 retry success |
| linkedin:J4 | 2026-10-04T08:00:00Z | 2026-10-04T08:00:00Z | Pending | Pending | None | Missing location |

Separate Attempted and Confirmed Notice Log:

| Key | Attempt Time | Event Type | Service Response | Resulting State |
| --- | --- | --- | --- | --- |
| linkedin:J3 | 2026-10-03T08:05:00Z | Delivery Attempt | 500 Rejected | Failed |
| linkedin:J3 | 2026-10-04T08:05:00Z | Delivery Retry | 200 Accepted | Sent |

Activity Summary:

- Unseen IDs on Oct 3: 1 (`J3`)
- Unseen IDs on Oct 4: 1 (`J4`)
- Confirmed provider actions across those days: 1 (`J3` retry)
- Total tracked keys: 4

## Final Action

To start getting repeat-safe alerts, open the [LinkedIn Jobs Scraper](https://apify.com/chronometrica/linkedin-jobs-scraper) or its preset [Scrape Fresh LinkedIn Jobs From the Last 7 Days](https://apify.com/chronometrica/linkedin-jobs-scraper/examples/scrape-fresh-linkedin-jobs-from-the-last-7-days) task, set a single target keyword with a small item cap, and build your tracking table before running your next collection batch.

## Sources

- [LinkedIn Jobs Scraper Source Actor](https://apify.com/chronometrica/linkedin-jobs-scraper)
- [Scrape Fresh LinkedIn Jobs Task Preset](https://apify.com/chronometrica/linkedin-jobs-scraper/examples/scrape-fresh-linkedin-jobs-from-the-last-7-days)
- [Community Discussion on Duplicate Job Alerts](https://www.reddit.com/r/n8nforbeginners/comments/1teylba/built_a_linkedin_job_notifier_using_n8n_local_db/)