# Indeed Job Details Scraper - Title, Company, Salary by URL (`tidytools/indeed-job-details`) Actor

Paste public Indeed job URLs you already have and get one JSON row per job: title, employer, location, job type, salary text, description and the job URL. Pay the posting does not show stays null. Not a job search. Failed or blocked pages are free.

- **URL**: https://apify.com/tidytools/indeed-job-details.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** Jobs, AI, Automation
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 extracted pages

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Indeed Job Details Scraper — Title, Company, Salary by URL

Paste public **Indeed job detail URLs you already have** (the `viewjob?jk=…` links, on any Indeed country site) and get one JSON row per job with fixed fields: job title, employer, location, job type, salary text, description and the job URL. Same columns on every row, nothing to configure.

This is **not** an Indeed job search and does not crawl Indeed: it reads only the URLs you give it. To collect job links, use your own list, an export, or another tool, then paste or pass them here.

### Input example

```json
{
  "urls": [
    "https://www.indeed.com/viewjob?jk=4f33002e695fbda3",
    "https://ph.indeed.com/viewjob?jk=326367db14f11b31"
  ]
}
```

The **Job posting** preset is already selected and **Extra fields** is empty; just paste URLs, one per line. The prefilled URL is a public example that was readable when checked; job postings expire, so replace it with yours. You can also pass `startUrls` or a `datasetId` with the URLs from another Actor's run.

### Output fields

Each successful job page is one Dataset row; the job is in its `data` object:

| Field | Type | Meaning |
|---|---|---|
| `title` | string | Job title as printed |
| `employer` | string | Hiring company named in the posting |
| `location` | string | Workplace as printed (city, region or Remote) |
| `employmentType` | string | Job type as printed, e.g. `Full-time`, `Part-time`, `Contract` |
| `salaryText` | string | Pay exactly as printed for this job, e.g. `"₱25,000 - ₱30,000 a month"`; `null` when the posting shows no pay |
| `description` | string | Job description text (duties, requirements, benefits) |
| `jobUrl` | string | The job URL that was read |

Only the main job on the page is read. Similar jobs, related jobs, "People also viewed" cards and other companies' salaries are ignored: `title`, `employer`, `location`, `employmentType` and `salaryText` must be printed in the posting itself, otherwise they are `null`. Most words of `description` must come from the posting itself too; a description taken from another job card is `null`. Pay is never estimated from the title, location or other jobs. The row also has `url`, `title` (page title), `success`, `charged`, `completeness`, `missingFields` and `fieldEvidence` (where each value was found). Turn on **Flat columns** for one column per field (Google Sheets, n8n, CSV).

Illustrative row (shape only, not a real posting):

```json
{
  "url": "https://ph.indeed.com/viewjob?jk=0123456789abcdef",
  "data": {
    "title": "Customer Service Representative",
    "employer": "Example Outsourcing Inc.",
    "location": "Makati, Metro Manila",
    "employmentType": "Full-time",
    "salaryText": null,
    "description": "Answer customer calls and emails, log cases and follow up until resolved. Requirements: ...",
    "jobUrl": "https://ph.indeed.com/viewjob?jk=0123456789abcdef"
  },
  "success": true,
  "charged": true
}
```

### Pricing

| Event | Price (Free plan) | When |
|---|---|---|
| `page-extracted` | $0.01 per job page | One successful job page (all fields of that page, one event) |

Free, never charged: pages that cannot be read (blocked by Indeed, bot check, 404, login, or disallowed by robots.txt when **Skip pages robots.txt disallows** is on), pages with no job title for their job (not a job detail page, `errorType: no_data`), timeouts, invalid URLs (a free row with `success: false`, `charged: false`, `errorType` and `error`), and repeated URLs (read once, counted in `SUMMARY.duplicatesSkipped`). Two readable job URLs = two events. Higher Apify plans get the engine's usual tier discounts. No new event or price is introduced: this is the AI Web Data Extractor engine with its `job_detail` preset.

### Limitations

Indeed's robots.txt does not allow general automated readers on its job pages (`Disallow: /viewjob` for `User-agent: *`, checked 2026-10-30). By default this Actor still reads the job URLs you give it, one page per URL, as when you open them yourself; it never logs in and never solves CAPTCHAs. You are responsible for using it in line with Indeed's terms; turn on **Skip pages robots.txt disallows** to follow robots.txt instead (each Indeed job page then returns a free `robots_disallowed` row). Pages that Indeed blocks or answers with a verification check, and pages that take too long, return a free `blocked`, `timeout` or error row. The number of successful rows can therefore be lower than the number of URLs, down to none. Use only public job pages you may access; pages behind a login are not read. Indeed search result pages are not supported (paste the job links instead). A very long description may be shortened; check important values against the source.

### FAQ

**Does it search Indeed for me?** No. It reads job URLs you already have. It does not run keyword searches or crawl result pages.

**Why is `salaryText` null?** The posting does not print pay for that job. Salaries shown only in similar or related job cards belong to other jobs and are not used.

**Can I add fields?** Yes: put them in **Extra fields**, e.g. `{"benefits": "array"}`. A full `jsonSchema` replaces the job fields entirely.

**Is the same URL charged twice?** No. Repeated URLs in one run are read once.

# Actor input Schema

## `urls` (type: `array`):

Paste public Indeed job detail URLs, one per line (for example https://www.indeed.com/viewjob?jk=... or a country site such as ph.indeed.com). The sample is one public job; replace it with yours. Search result pages and pages behind a login are not supported. Repeated URLs are read once. Invalid URLs return a free error row. You can also use startUrls or a datasetId.

## `startUrls` (type: `array`):

Same as "Indeed job URLs", in Apify's request list format (also accepts a link to a text file with URLs). Kept for existing integrations; new integrations should use "urls".

## `preset` (type: `string`):

Job posting is selected: title, employer, location, employmentType, salaryText, description and jobUrl. Only the main job on the page is read; similar or related job cards are ignored. Pay the posting does not print is null. Extra Fields are merged and your types win.

## `fields` (type: `object`):

Leave empty for the job fields. Add columns such as {"benefits": "array"} to extend them (your types win); advanced JSON Schema replaces both.

## `instructions` (type: `string`):

Optional hints, such as "Description in under 100 words".

## `outputMode` (type: `string`):

The job posting preset always returns one row per page. Values: page = One row per page; items = One row per item on the page (lists, for other presets).

## `flatOutput` (type: `boolean`):

Put every job field in its own column instead of inside "data". Every row has the same columns in the same order, failed pages included (empty values, recordCount 0). Same number of rows and same price as without it.

## `qualityReport` (type: `boolean`):

Write DATA_QUALITY.json and DATA_QUALITY.csv with field missing counts, type error counts, review suggestions and completeness across saved data rows. Failed or free rows are counted separately. Quality hints are not human verification. No extra AI calls or page-extracted charges.

## `jsonSchema` (type: `object`):

Optional full JSON Schema (type: object). When set, it replaces the job fields and Extra fields.

## `datasetId` (type: `string`):

Read the job URLs from another Actor run's dataset, e.g. a list of job links you collected earlier.

## `datasetField` (type: `string`):

Field with the URLs, e.g. "url" (dot paths such as "job.url" work). Empty = the first URL field found.

## `mode` (type: `string`):

"Auto" reads pages over plain HTTP and switches to a real browser when a site blocks it or needs JavaScript. Values: auto = Auto (recommended); fast = Fast (HTTP only); browser = Browser (JavaScript rendering).

## `maxConcurrency` (type: `integer`):

How many pages are processed at the same time.

## `itemTimeoutSecs` (type: `integer`):

A page that takes longer (reading + AI) is stopped and returned as a free error row with errorType "timeout". 0 = no limit.

## `aiModel` (type: `string`):

Same price for every model.

## `httpVia` (type: `string`):

Some sites block requests from data centers. Auto retries from a second network before falling back to a real browser. If both are refused, Auto also tries our second server (Oracle, different IP) before a real browser.

## `proxyConfiguration` (type: `object`):

Only used for requests sent from Apify's network. Apify proxy usage is billed to your Apify account.

## `respectRobotsTxt` (type: `boolean`):

Off (default): the job URLs you give are read even though Indeed's robots.txt disallows automated readers on job pages, as when you open them yourself. We never log in and never solve CAPTCHAs; a page that shows a block or verification check is a free error row. On: such pages are not read and return a free robots_disallowed row.

## Actor input object example

```json
{
  "urls": [
    "https://www.indeed.com/viewjob?jk=4f33002e695fbda3"
  ],
  "preset": "job_detail",
  "outputMode": "page",
  "flatOutput": false,
  "qualityReport": false,
  "mode": "auto",
  "maxConcurrency": 5,
  "itemTimeoutSecs": 180,
  "aiModel": "auto",
  "httpVia": "auto",
  "respectRobotsTxt": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `full` (type: `string`):

No description

## `qualityJson` (type: `string`):

No description

## `qualityCsv` (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 = {
    "urls": [
        "https://www.indeed.com/viewjob?jk=4f33002e695fbda3"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/indeed-job-details").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 = { "urls": ["https://www.indeed.com/viewjob?jk=4f33002e695fbda3"] }

# Run the Actor and wait for it to finish
run = client.actor("tidytools/indeed-job-details").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 '{
  "urls": [
    "https://www.indeed.com/viewjob?jk=4f33002e695fbda3"
  ]
}' |
apify call tidytools/indeed-job-details --silent --output-dataset

```

## MCP server setup

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

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/o4deaiM9lVhg9SBT0/builds/0VToItdJimmjS4gyT/openapi.json
