# Job Feed Check: Duplicates, Fields and Changes (`candied_breakwater/jobfeed-check`) Actor

Paste a job spreadsheet or CSV to remove exact duplicates, flag missing fields and compare supplied snapshots. Get checked rows and next-step guidance. No scraping. Advanced JSON remains supported.

- **URL**: https://apify.com/candied\_breakwater/jobfeed-check.md
- **Developed by:** [Herry](https://apify.com/candied_breakwater) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 checked job records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Check a job export before it reaches your users

### Start here — no JSON required

1. Copy your spreadsheet including its header row and paste it into **Current spreadsheet**. Excel/Google Sheets tab-separated cells and CSV text both work. No JSON is needed.
2. Set the run charge limit to cover your small sample, then click **Start**.
3. Open the results table. Read **Result summary** and **Next step**, then export using **Download CSV** or **Download Excel** in the output links.

Pasteable example:

```text
id,source,company,title,location,url
001,example,Example Co,Engineer,London,https://example.com/jobs/1
001,example,Example Co,Engineer,London,https://example.com/jobs/1
002,example,Example Co,Designer,Remote,https://example.com/jobs/2
```

Use headers such as id, source, company, title, location and url. Common names such as Job ID, Company Name, Job Title and Job URL are recognized. IDs keep leading zeros. Optional remote accepts true/false, yes/no or 1/0; employeeCount accepts non-negative whole numbers. Blank cells stay unknown. For custom headers, set fieldMap in Advanced JSON. Keep the same header conventions/mapping for both snapshots. Paste an earlier table in Previous spreadsheet only if you need comparison; matching scope labels and explicit completeness flags still control missing-record comparisons.

**Sample cost:** For a first test, use the three-row sample and set the maximum run charge to at least $0.01. Exact duplicate removal leaves two charged rows ($0.00205 including start). 1,000 retained rows cost $1.00005; a $1.01 cap covers that example. Platform usage is included. These examples are independent technical samples, not customers or partners. To use Advanced JSON, clear the beginner text fields; do not fill two input methods at once. Existing API inputs remain supported.

Only treat results as complete when the run succeeded and OUTPUT.status is complete. An unknown result needs review; it is not a verified negative. Start small: network timeouts and access blocks can still occur.

Job Feed Check checks **supplied spreadsheet, CSV or JSON job exports** for duplicate identities, missing fields, filter uncertainty and differences between comparable snapshots. It does not scrape websites, enrich records, or verify whether a job is still open.

Use it after a job scraper and before an import, a weekly hiring report, or a job-board update.

### What you get

- A dataset of normalized records with filter status, change observations and field findings.
- `REPORT`: the full JSON report, including duplicate groups and records requiring review.
- `SUMMARY`: counts, field coverage and limitations.
- `OUTPUT`: a completion marker written only after all outputs finish.

### Pricing

**$1 per 1,000 checked job records** saved to the output dataset ($0.001 each), plus **$0.00005 per run start** at the supported memory settings. Platform usage is included in this event price.

All retained output records count, including records marked `excluded`, `unknown`, or flagged for review. Exact duplicates removed before output are not charged separately. Records in the `previous` snapshot are not charged unless also present as retained current output. There is no separate event fee for the JSON report or summary. For example, a completed run with 1,000 output records costs $1.00005 at these prices.

Set your maximum run charge above the expected output charge plus the start fee. Before writing results, the Actor checks that your budget covers all retained records at the run's configured prices. If it does not, the run fails without writing or charging for result records; the start fee can still apply. A later storage failure or interruption can leave partial, already charged results. Only import a complete export when the run succeeded and `OUTPUT.status` is `complete`. If the completion marker is absent, do not treat the dataset as complete. Start a new run to retry; do not resurrect a run that has already written results. Each new run is charged separately for its delivered output.

### Quick example

```json
{
  "current": [
    {"source":"example","company":"Example Co","id":"1","title":"Engineer","location":"US","url":"https://example.com/jobs/1","remote":true},
    {"source":"example","company":"Example Co","id":"1","title":"Engineer","location":"US","url":"https://example.com/jobs/1?utm_source=duplicate","remote":true}
  ]
}
```

This synthetic example returns one retained record and one exact duplicate removal. Salary is reported as missing because the input did not provide it. No salary is guessed.

### Identity and uncertainty

A stable source/company/id combination identifies a record; otherwise the source URL is used. URL tracking parameters beginning with `utm_` and fragments are removed, while business query parameters are retained. Input IDs are case-sensitive.

Repeated identities are collapsed only when tracked fields match. Conflicting records remain visible. Records with the same company, title and location but different identities are flagged as possible duplicates and **not deleted**. Separate real vacancies can share the same title.

### Fields and mapping

Canonical flat fields: `id`, `source`, `company`, `title`, `location`, `url`, `salary` (text), `remote` (boolean), `employeeCount` (nonnegative integer). Use `fieldMap` for alternate flat names. Extra columns are omitted from normalized rows; preserve your original file. Nested values are flagged rather than interpreted automatically.

`filters` supports `titleIncludes`, `locationIncludes`, `remoteOnly`, `minEmployees`, `maxEmployees`. Conditions are ANDed. Each row is `matched`, `excluded` or `unknown`. This label only describes the configured filters, not whether a record is correct or a person is eligible for the role. All retained rows remain in the dataset for review.

### Comparing snapshots

Provide `previous` and matching non-empty `currentScope` / `previousScope` strings to compare. The scope should describe the same sources, companies and upstream selection rules. New-to-input records are not necessarily newly published jobs.

Both completeness flags must also be true to list missing records. Do not mark a truncated or failed scrape as complete. A missing record is only absent from the supplied export; it is **not verified closed**. No persistent monitoring state is created, and different runs are not automatically compared.

### Limits and data handling

- Up to 5,000 rows per snapshot; 5 MB serialized input.
- 512 MB default memory and a 60-second default runtime limit.
- The Actor reads its input and its own run's pricing, and reads/writes its default Apify stores. It does not contact job sites or a third-party AI service.
- Input and output are stored in the initiating account's Apify storage, subject to its access settings and retention policy. Unlike the local browser demo, this version processes data on Apify.
- A failed write or incomplete batch readback fails the run. Dataset writes are not automatically retried, to avoid uncertain duplicate delivery. A nonempty output dataset is rejected before new billable writes. Use the run status and completion marker before importing output.

### Current status

This is an early version with automated tests for core behavior and dataset delivery. It uses explicit rules rather than AI judgments. It does not verify live vacancy status, classify employer legitimacy, or determine worldwide remote eligibility.

For a bug report, use the Actor's Issues tab and include the input shape, expected result and actual result. Remove confidential information from shared examples. No customer outcomes or time savings are promised.

# Actor input Schema

## `currentCsv` (type: `string`):

Include headers and at least one data row. Up to 5,000 rows; paste text, not a filename. Leave advanced current empty.

## `previousCsv` (type: `string`):

Paste an earlier export with headers. For comparisons, set matching scope labels under Advanced JSON.

## `current` (type: `array`):

For API users. Clear Current spreadsheet before supplying this array.

## `previous` (type: `array`):

Optional earlier snapshot. Missing records never establish job closure.

## `fieldMap` (type: `object`):

Map canonical names to flat source column names, e.g. {"title":"jobTitle","url":"applyUrl"}. Nested mapping is not supported.

## `filters` (type: `object`):

Optional titleIncludes, locationIncludes, remoteOnly, minEmployees, maxEmployees. AND semantics. Unknown values remain unknown. Location is text matching, not eligibility.

## `currentScope` (type: `string`):

Stable label describing the exact companies, sources and upstream filters. Matching previousScope enables comparison.

## `previousScope` (type: `string`):

Must exactly match currentScope to compare. Do not match labels for differently filtered inputs.

## `currentComplete` (type: `boolean`):

Only assert this for a full successful export, not a capped sample or failed scrape.

## `previousComplete` (type: `boolean`):

Both completeness flags and matching scopes are needed to list absent records; these are caller assertions.

## Actor input object example

```json
{
  "currentCsv": "id,source,company,title,location,url\n001,example,Example Co,Engineer,London,https://example.com/jobs/1\n001,example,Example Co,Engineer,London,https://example.com/jobs/1\n002,example,Example Co,Designer,Remote,https://example.com/jobs/2",
  "fieldMap": {},
  "filters": {},
  "currentComplete": false,
  "previousComplete": false
}
```

# Actor output Schema

## `records` (type: `string`):

No description

## `report` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `excel` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "currentCsv": `id,source,company,title,location,url
001,example,Example Co,Engineer,London,https://example.com/jobs/1
001,example,Example Co,Engineer,London,https://example.com/jobs/1
002,example,Example Co,Designer,Remote,https://example.com/jobs/2`
};

// Run the Actor and wait for it to finish
const run = await client.actor("candied_breakwater/jobfeed-check").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "currentCsv": """id,source,company,title,location,url
001,example,Example Co,Engineer,London,https://example.com/jobs/1
001,example,Example Co,Engineer,London,https://example.com/jobs/1
002,example,Example Co,Designer,Remote,https://example.com/jobs/2""" }

# Run the Actor and wait for it to finish
run = client.actor("candied_breakwater/jobfeed-check").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "currentCsv": "id,source,company,title,location,url\\n001,example,Example Co,Engineer,London,https://example.com/jobs/1\\n001,example,Example Co,Engineer,London,https://example.com/jobs/1\\n002,example,Example Co,Designer,Remote,https://example.com/jobs/2"
}' |
apify call candied_breakwater/jobfeed-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,candied_breakwater/jobfeed-check"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/dnMwWRwE3r2yIBGCY/builds/lsMmekhl0ghFyvcbC/openapi.json
