# CutShort Jobs Scraper (`memo23/cutshort-jobs-scraper`) Actor

Scrape CutShort.io India tech & startup jobs — title, both salary bands (displayed + hidden internal floor), skills, experience, full description, recruiter name, remote type, company funding stage. Paste listing or job URLs, or search by skill + city. No login. JSON or CSV out.

- **URL**: https://apify.com/memo23/cutshort-jobs-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Automation, AI
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## CutShort Jobs Scraper

Scrape India's tech & startup hiring market from cutshort.io — full job descriptions, real salary bands (including the internal range the site doesn't display), skills, recruiter names, and company funding data. Paste listing URLs, direct job URLs, or just type skill keywords like `react` + `bangalore`. Every row comes from CutShort's own backend JSON API, so fields arrive structured and complete — no HTML guesswork.

#### How it works

![How CutShort Jobs Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-cutshort.png)

#### ✨ Why use this scraper?

- **Pure JSON API** — every field comes from CutShort's backend API, the same one the site itself renders from. No brittle CSS selectors, no HTML drift.
- **Two salary bands per job** — the publicly displayed range AND the internal `min`/`max` band, which is often wider at the bottom (e.g. displayed ₹32L–₹47L, internal floor ₹16L).
- **Keyword search built in** — type `python`, `react`, `devops`; the actor resolves keywords against CutShort's ~3,700 live listing categories and picks the right one, including `-in-{city}` and `remote-` variants.
- **Staffing-agency unmasking** — when a recruiter posts for a hidden client ("Service Co"), the row still carries the actual posting agency in `staffingAgencyName`.
- **Recruiter names on every row** — the human who posted the job, useful for outreach and ATS enrichment.
- **50 jobs per HTTP call** — listing responses arrive as complete rows, so runs are fast and cheap; no per-job fetches unless you opt into `fullDetails`.

#### 🎯 Use cases

| Who | What they do with it |
|---|---|
| Recruiters & sourcing teams | Track which companies and agencies are hiring for which stacks, with recruiter names for outreach |
| Salary-benchmark analysts | Build India tech compensation datasets from the internal (not just displayed) salary bands |
| Job aggregators | Ingest structured India startup jobs with full descriptions, skills, and remote flags |
| VC / market researchers | Monitor hiring velocity by company stage (`Bootstrapped`, `Raised funding`, `Profitable`) via `fullDetails` |
| Job seekers & career tools | Watch fresh postings for a skill + city combo with the recency filter |

#### 📥 Supported inputs

| Input | Example | What happens |
|---|---|---|
| Listing / category URL | `https://cutshort.io/jobs/python-jobs` | Walks the category page by page, 50 jobs per call |
| Listing with city | `https://cutshort.io/jobs/reactjs-jobs-in-bangalore-bengaluru` | Same, scoped to the city |
| Direct job URL | `https://cutshort.io/job/Senior-AI-Fullstack-Developer-Ande-ai-aXaLhwPI` | Fetches that one job via the detail API |
| Search keyword | `react`, `product manager`, `golang` | Resolved to the best-matching category slug from the live sitemap |
| Keyword + city | `react` + city `bangalore` | Resolves to the city variant listing |
| Keyword + remoteOnly | `golang` + `remoteOnly: true` | Prefers CutShort's dedicated `remote-golang-jobs` listing and filters rows to `remote_only` |

**Not supported:** company profile pages (`/companies/...`), the `/jobs` root hub (it's a category directory, not a job list), and any URL behind a CutShort login.

#### 🔄 How it works

1. **Classify** every start URL: listing/category vs direct job. Keywords are resolved to category slugs against CutShort's live sitemap (~3,700 base categories, ~17,000 city variants).
2. **Fetch listings** via the backend JSON API — each page returns 50 complete job rows (title, salary, skills, description, recruiter, company, remote type).
3. **Optionally deepen** each job via the detail API (`fullDetails`): posted date, company description, funding stage, size, founded year, tech stack, follower count.
4. **Filter** by `remoteOnly` and/or `postedWithinHours` when set.
5. **Push one normalized row per job** — canonical core fields first, CutShort-specific extras after.

#### ⚙️ Input parameters

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | `["https://cutshort.io/jobs/python-jobs"]` | CutShort listing URLs and/or direct job URLs, mixed freely |
| `searchQueries` | array | `[]` | Skill/role keywords resolved to listing categories |
| `city` | string | — | City applied to every keyword (`bangalore`, `pune`, `mumbai`, …) |
| `remoteOnly` | boolean | `false` | Only fully-remote jobs |
| `fullDetails` | boolean | `false` | One extra API call per job → posted date + full company block |
| `enrichEmails` | boolean | `false` | Experimental employer-email discovery (billed per found email) |
| `postedWithinHours` | integer | — | Keep only jobs with hiring activity in the last N hours (auto-enables `fullDetails`) |
| `maxItems` | integer | `1000` | Hard cap on rows |
| `maxConcurrency` | integer | `10` | Parallel detail-API calls |
| `proxy` | object | direct | Not needed for CutShort; configure only to force all traffic through your proxy |

#### 📊 Output overview

One row per job. The canonical core (identity, company, location, salary, dates, description, apply flow) always appears with the same field names as our other job-board actors, so multi-board pipelines map one column layout. CutShort-specific extras (both salary bands, experience range, recruiter, workplace type, company funding block) follow after the core.

#### 📦 Output sample

Real row (trimmed) from a `python-jobs` listing run:

```json
{
  "type": "job",
  "source": "cutshort.io",
  "jobId": "6a748e95d9c6d17c4fa75201",
  "jobUrl": "https://cutshort.io/job/Principal-Lead-Data-Architect-AWS-Focus-Bengaluru-Bangalore-Vikash-Technologies-bCOF8H2B",
  "title": "Principal / Lead Data Architect(AWS Focus)",
  "companyName": "Service Co",
  "location": "Bengaluru (Bangalore)",
  "remote": false,
  "salary": { "currency": "INR", "min": 1600000, "max": 4700000, "raw": "₹32L - ₹47L / yr", "unit": "year" },
  "salaryDisplayedMin": 3200000,
  "salaryDisplayedMax": 4700000,
  "employmentTypes": ["full-time"],
  "description": "<p>Hiring for Prinipal / Lead Data Architect</p><p>Exp : 12 - 16 yrs</p>…",
  "descriptionText": "Hiring for Prinipal / Lead Data Architect\nExp : 12 - 16 yrs\nWork Location : Bengaluru…",
  "applyType": "internal",
  "applyUrl": "https://cutshort.io/job/Principal-Lead-Data-Architect-AWS-Focus-Bengaluru-Bangalore-Vikash-Technologies-bCOF8H2B",
  "shortId": "bCOF8H2B",
  "skills": ["databricks", "Python", "Scala", "SQL", "Snowflake", "Structured Streaming", "Apache Kafka", "flink", "Amazon Web Services (AWS)"],
  "workplaceType": "onsite",
  "experienceMinYears": 12,
  "experienceMaxYears": 16,
  "recruiterName": "Rishika Teja",
  "hiringForClient": true,
  "staffingAgencyName": "Vikash Technologies",
  "logoUrl": "https://cdn.cutshort.io/public/images/default_company_picture.jpg",
  "companyProfileUrl": "https://cutshort.io/company/vikash-technologies-16-88qXblEn",
  "scrapedAt": "2026-08-08T12:55:21.462Z"
}
```

With `fullDetails: true` each row additionally carries values like:

```json
{
  "postedDate": "2026-08-07T13:52:22.758Z",
  "companyDescription": "Ande is an AI-native, full-stack TypeScript platform built on React, Node.js, GraphQL…",
  "companyStage": "Raised funding",
  "companySize": "0-20",
  "companyType": "Product",
  "companyFounded": 2023,
  "companyTechStack": ["NodeJS (Node.js)", "React.js", "Artificial Intelligence (AI)"],
  "companyFollowersCount": 1363
}
```

#### 🗂 Key output fields

| Group | Fields |
|---|---|
| Identity | `jobId`, `shortId`, `slug`, `jobUrl`, `title`, `source` |
| Compensation | `salary.{currency,min,max,raw,unit}` (internal band), `salaryDisplayedMin`, `salaryDisplayedMax`, `salaryRaw` |
| Role | `skills[]`, `employmentTypes[]`, `categories[]`, `experienceMinYears`, `experienceMaxYears` |
| Workplace | `location`, `locations[]`, `remote`, `remoteType`, `workplaceType` (`remote` / `remote-allowed` / `onsite`) |
| People | `recruiterName`, `recruiterAvatar`, `hiringForClient`, `staffingAgencyName` |
| Company | `companyName`, `companyAlias`, `companyProfileUrl`, `logoUrl`, `companyWebsite`, `companyDomain` |
| Company (fullDetails) | `companyDescription`, `companyStage`, `companySize`, `companyType`, `companyFounded`, `companyTechStack[]`, `companyFollowersCount`, `postedDate` |
| Content & apply | `description` (HTML), `descriptionText` (plain), `applyType`, `applyUrl` |
| Meta | `scrapedAt`, `fullDetailsFetched`, `jobValid` |

#### ❓ FAQ

**Do I need a proxy?**
No. cutshort.io has no anti-bot protection; the actor connects directly and only falls back to a built-in residential proxy on retries. Configure the proxy input only if you want all traffic through your own.

**What's the difference between `salary` and `salaryDisplayedMin/Max`?**
CutShort stores an internal salary band and a "vanity" band that the site actually displays. `salary.min`/`salary.max` is the internal band — its floor is frequently lower than what candidates see. `salaryDisplayedMin/Max` matches the public display, and `salary.raw` is the display string (e.g. `₹32L - ₹47L / yr`).

**How do search keywords work?**
The actor downloads CutShort's live listings sitemap (~3,700 categories) once per run and fuzzy-matches your keyword to the best category — `react js` lands on `reactjs-jobs`, `golang` + `remoteOnly` on `remote-golang-jobs`. The chosen slug is logged. If nothing matches, the keyword is skipped with a warning.

**Why is `companyName` sometimes generic, like "Service Co"?**
Staffing agencies posting for confidential clients get a masked client name. The row still includes `hiringForClient: true` and the real posting agency in `staffingAgencyName`.

**How do I get posted dates?**
`postedDate` comes from the detail API — enable `fullDetails` (or set `postedWithinHours`, which enables it automatically). It reflects the job's latest hiring-intent activity, which is CutShort's freshness signal.

**How fresh is the data?**
CutShort serves its API through a CDN cache of up to ~4 hours, so rows can lag the site by that much. For monitoring runs, `postedWithinHours: 24` daily is a comfortable cadence.

**How do I control cost?**
Set `maxItems`. Listing runs are extremely efficient — 50 complete rows per HTTP call — and `fullDetails` is the only per-row fetch.

#### 💬 Support

- For issues or feature requests, please use the **Issues** tab on the actor's Apify Console page.
- Author's website: <https://muhamed-didovic.github.io/>
- Email: <muhamed.didovic@gmail.com>

#### 🛠 Additional services

- Custom output shape, additional fields, or one-off datasets: <muhamed.didovic@gmail.com>
- Need a similar scraper for other job boards (Naukri, LinkedIn, Indeed, Instahyre, etc.)? Drop an email.
- For API access (no Apify fee, just a usage fee for the API): <muhamed.didovic@gmail.com>

#### 🔎 Explore more scrapers

If this CutShort Jobs Scraper was useful, see other scrapers and actors at [memo23's Apify profile](https://apify.com/memo23) — covering job boards, real estate, social media, and more.

#### 🤖 For AI Agents & LLM Apps

This actor is MCP-ready: call it from AI agents via the [Apify MCP server](https://mcp.apify.com) or the Apify API, using the same input schema documented above. Typical agent patterns:

- **Talent-market questions** — "What do senior React roles in Bangalore pay?" → run with `searchQueries: ["react"]`, `city: "bangalore"`, read `salary` + `experienceMinYears` off the rows.
- **Company research** — feed a direct job URL with `fullDetails: true` to get funding stage, size, founded year, and tech stack alongside the role.
- **Fresh-postings watch** — schedule with `postedWithinHours: 24` and pipe new rows into your agent's context or a vector store.

Structured, deduplicated JSON rows with stable field names make the output safe to map directly into LLM tool responses.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by CutShort (cutshort.io) or any of its subsidiaries or affiliates. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available job-listing data — the same backend JSON API that renders CutShort's public SEO pages. It uses no authenticated endpoints, recruiter-only features, or content behind a CutShort login. Users are responsible for ensuring their use complies with CutShort's Terms of Service, applicable data-protection law (GDPR, India's DPDP Act, CCPA, etc.), and any contractual obligations of their own organisation.

***

### SEO Keywords

cutshort scraper, scrape cutshort, cutshort api, cutshort.io scraper, Apify cutshort, cutshort jobs scraper, india tech jobs scraper, india startup jobs api, india jobs data, bangalore tech jobs scraper, india salary benchmark data, india tech salaries dataset, indian recruitment data, india hiring trends, tech recruiter data india, india job board scraper, remote india jobs scraper, naukri alternative scraper, instahyre alternative scraper, india startup hiring data, cutshort job listings api

# Actor input Schema

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

Full cutshort.io URLs to crawl. Listing/category URLs and direct job URLs both work.

## `searchQueries` (type: `array`):

Skill or role keywords — e.g. `react`, `python`, `product manager`, `devops`. Each keyword is matched against cutshort's ~3,700 listing categories (live from the sitemap) and scraped as its own listing. Combine with the city filter below to target e.g. React jobs in Bangalore.

## `city` (type: `string`):

Optional Indian city applied to every search keyword — e.g. `bangalore`, `mumbai`, `pune`, `delhi`, `hyderabad`, `chennai`. Matched against cutshort's `-in-{city}` listing variants; falls back to the all-India listing when no city variant exists.

## `remoteOnly` (type: `boolean`):

Only return fully-remote jobs. Uses cutshort's dedicated `remote-{skill}-jobs` listings when they exist, and additionally filters every row by `remoteType = remote_only`.

## `fullDetails` (type: `boolean`):

When enabled, each job additionally fetches cutshort's job-detail API to add `postedDate`, `companyDescription`, `companyFounded`, `companyStage` (e.g. "Raised funding"), `companySize`, `companyTechStack`, and `companyFollowersCount`. Costs one extra HTTP call per job. Listing rows already include title, salary, skills, full description, recruiter, and location without this.

## `enrichEmails` (type: `boolean`):

If enabled, tries to find a contact email for each employer by discovering the company's website (Clearbit) and reading its contact/about pages. Adds contactEmail + contactWebsite columns plus a detailed emailEnrichment object. Best-effort, billed per contact email found; only charged when an email is returned, never for misses.

## `postedWithinHours` (type: `integer`):

Filters on each job's hiring-intent date from the detail API (automatically enables the full-details fetch). Set 24 for the last day, 168 for the last week. Leave empty (or 0) to return everything.

## `maxItems` (type: `integer`):

Hard cap on the number of jobs collected. CutShort lists tens of thousands of jobs across ~3,700 categories; use this cap to limit billing.

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

Maximum number of detail API calls processed in parallel.

## `minConcurrency` (type: `integer`):

Minimum number of requests processed in parallel (Crawlee fallback path only).

## `maxRequestRetries` (type: `integer`):

Number of retries before a failed request is given up.

## `proxy` (type: `object`):

cutshort.io has no anti-bot protection, so no proxy is needed: the actor connects directly and falls back to a built-in residential proxy on retries. Only configure a proxy here if you specifically want all traffic routed through it.

## Actor input object example

```json
{
  "startUrls": [
    "https://cutshort.io/jobs/python-jobs"
  ],
  "searchQueries": [],
  "remoteOnly": false,
  "fullDetails": false,
  "enrichEmails": false,
  "maxItems": 1000,
  "maxConcurrency": 10,
  "minConcurrency": 1,
  "maxRequestRetries": 5,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "startUrls": [
        "https://cutshort.io/jobs/python-jobs"
    ],
    "searchQueries": [],
    "city": "",
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/cutshort-jobs-scraper").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 = {
    "startUrls": ["https://cutshort.io/jobs/python-jobs"],
    "searchQueries": [],
    "city": "",
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/cutshort-jobs-scraper").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 '{
  "startUrls": [
    "https://cutshort.io/jobs/python-jobs"
  ],
  "searchQueries": [],
  "city": "",
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call memo23/cutshort-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/cutshort-jobs-scraper"
        }
    }
}

```

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/lYzH7NjSlo4ZF6toq/builds/cB9rBngVIFJ6esdez/openapi.json
