# Internshala Jobs Scraper & Internships Monitor (`khadinakbar/internshala-jobs-internships-monitor`) Actor

Scrape Internshala jobs and internships with salary, stipend, skills, public applicants and employer hiring history. Monitor new and changed listings with field-level changes, pay and applicant deltas, monthly pay comparisons and deadline status. Pay per event plus platform usage.

- **URL**: https://apify.com/khadinakbar/internshala-jobs-internships-monitor.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 validated listing returneds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

### What does this Internshala scraper do?

Extract public Internshala jobs and internships into structured records, or monitor a saved search for new and changed postings. Start with a role keyword, a public search URL, or an individual posting URL. Return company names, locations, original salary or stipend amounts, skills, and application links. Enable full details for descriptions, eligibility, perks, openings, and application deadlines when published.

This Actor serves campus career teams, recruiting researchers, and job-board operators building an early-career opportunity feed. Use snapshot mode for a current shortlist and monitor mode for incremental refreshes. For applications and account-specific information, continue on the posting's Internshala page.

### Output: one record per returned posting

| Fields | Meaning |
| --- | --- |
| `id`, `url`, `listingType` | Stable published ID, original posting link, job or internship |
| `title`, `companyName`, `locations` | Role, employer and published locations |
| `compensationText`, `compensationMin`, `compensationMax` | Original text and numeric lower/upper bounds |
| `compensationCurrency`, `compensationPeriod` | Original currency and year/month/week/day/lump-sum unit |
| `duration`, `isWorkFromHome`, `isHybrid`, `isPartTime` | Published work arrangements and duration |
| `skills`, `withJobOffer`, `activelyHiring` | Required skills and published hiring signals |
| `description`, `descriptionIsFull` | Card excerpt, or full description with details enabled |
| `perks`, `eligibility`, `openings`, `applyByDate` | Detail-page fields when published |
| `changeType` | `snapshot`, `new`, or `updated` |
| `firstSeenAt`, `scrapedAt`, `sourceUrl` | Monitor observation and capture provenance |

Missing published values remain `null` or empty arrays. Unpaid compensation is zero. Amounts retain their original period; annual job compensation and monthly stipends are not converted into comparable rates automatically. `postedText` retains relative page wording instead of inventing an exact posting date.

### Pricing

**Pay per event** applies to each validated listing returned to the Dataset, plus the Actor start event. In monitor mode, unchanged listings carry no listing event fee. A quiet initial baseline also has no listing event fee. **Apify platform usage is paid by the caller** in addition to the event fees. See this Actor's [Pricing tab](https://apify.com/khadinakbar/internshala-jobs-internships-monitor/pricing) for current event prices.

Use `maxResults` and Apify's `maxTotalChargeUsd` run option to bound event spending. `maxPages` limits scanning work, including pages that produce no matching or new listings.

### Quick start

Find public Python jobs and internships:

```json
{
  "mode": "scrape",
  "listingType": "both",
  "searchQuery": "python",
  "maxResults": 20,
  "maxPages": 2,
  "includeDetails": false
}
```

For city filtering, set `location` to a case-insensitive substring of a published location, such as `Bangalore`. `workFromHome` and `minCompensation` are also local filters over the scanned pages. Supply a filtered Internshala search URL through `startUrls` for narrower upstream coverage. Explicit URLs replace the generated keyword search.

Read a posting's full public detail page:

```json
{
  "startUrls": [
    {"url": "https://internshala.com/job/detail/fresher-sales-associate-job-in-multiple-locations-at-embtel-solutions1791884318"}
  ],
  "includeDetails": true,
  "maxResults": 1
}
```

A captured listing record illustrates the output shape:

```json
{
  "id": "3316161",
  "listingType": "job",
  "title": "Sales Associate",
  "companyName": "Embtel Solutions",
  "locations": ["Ambala", "Chandigarh", "Kharar", "Mohali", "Zirakpur", "Panchkula", "Punjab", "Himachal Pradesh", "Haryana"],
  "compensationText": "₹ 2,15,000 - 3,25,000 /year",
  "compensationMin": 215000,
  "compensationMax": 325000,
  "compensationCurrency": "INR",
  "compensationPeriod": "year",
  "skills": ["Lead Generation", "English Proficiency (Spoken)", "Market research", "Sales"],
  "descriptionIsFull": false,
  "changeType": "snapshot",
  "url": "https://internshala.com/job/detail/fresher-sales-associate-job-in-multiple-locations-at-embtel-solutions1791884318"
}
```

The example shows a captured posting, not a guarantee that this specific opportunity remains open. Inspect `descriptionIsFull` when selecting description fields.

### Workflow: monitor internships with a persistent baseline

A campus career team can establish a quiet baseline for Python internships, rerun it with the same watch settings, and add only new or changed opportunities to its shortlist. Compare `compensationPeriod` before ranking the resulting stipends, then send candidates to the original application links.

```json
{
  "mode": "monitor",
  "listingType": "internships",
  "searchQuery": "python",
  "watchId": "campus-python-internships",
  "emitInitial": false,
  "maxResults": 30,
  "maxPages": 2
}
```

The first run saves fingerprints without returning listing rows. Later runs return only new and materially changed records. Leave `emitInitial` enabled to return an initial feed immediately.

Keep `watchId`, filters, and detail mode stable between runs. By default the Actor creates an actor-owned state store scoped to your Apify account. `OUTPUT.stateStoreId` and `OUTPUT.monitorKey` identify the baseline. You can explicitly select another key-value store using `stateStoreId`; its input grants only read/write access to that resource. Different filter sets use separate baselines, and different watch IDs remain separate within a shared store.

The baseline retains fingerprints, bounded tracked posting values and timestamps for up to 10,000 postings, pruned after 30 days without an observation. A posting seen after that retention boundary may be emitted as new. A bounded scan cannot prove a posting closed, so the monitor emits new and updated events only.

Run one occurrence of each watch at a time. The Actor includes a best-effort overlap guard, but Apify key-value storage does not provide an atomic distributed lock. If a write or charge is interrupted ambiguously, the monitor stops and asks for reconciliation rather than replaying it. Inspect the previous Dataset, `STATE`, `RUN_SUMMARY`, and charge events before clearing unresolved state.

For recurring operation, create an [Apify schedule](https://docs.apify.com/platform/schedules) with the same input and ensure occurrences do not overlap. This Actor runs a single bounded scan per invocation; it does not create schedules or send notifications itself. Connect Dataset output to your own approved integration for delivery.

### API and CLI

Create a bounded asynchronous run. Keep your Apify token in an environment variable and send it through the authorization header:

```bash
curl --request POST \
  'https://api.apify.com/v2/acts/khadinakbar~internshala-jobs-internships-monitor/runs?maxTotalChargeUsd=0.10' \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"listingType":"internships","searchQuery":"python","maxResults":10}'
```

Read the returned run's `defaultDatasetId` after it finishes, then retrieve `/v2/datasets/DATASET_ID/items`. Read `OUTPUT` and `RUN_SUMMARY` from its `defaultKeyValueStoreId` for coverage and outcome information.

```bash
apify call khadinakbar/internshala-jobs-internships-monitor \
  --input '{"listingType":"jobs","searchQuery":"python","maxResults":10}'
```

### AI agent prompt

> Use `khadinakbar/internshala-jobs-internships-monitor` with listingType internships, searchQuery python, maxResults 10, and includeDetails false. After completion, retrieve the run's Dataset and OUTPUT. Return title, companyName, locations, compensationText, compensationPeriod, and url. Explain any partial coverage reported in OUTPUT.

For a tool-enabled workflow, use [Apify MCP](https://docs.apify.com/integrations/mcp) to inspect the Actor and input schema, then make a bounded call and retrieve the Dataset by its returned ID. Client-specific discovery and selection depend on your client configuration.

### Best results and recovery

- Start with a small result cap and a focused keyword or filtered search URL.
- Enable full detail requests when your workflow needs complete descriptions and deadlines.
- Increase `maxPages` when local filters or unchanged records reduce your output.
- Keep original salary/stipend periods when preparing compensation comparisons.
- Use one monitor occurrence at a time and retain a stable watch ID.

Every terminal run writes `OUTPUT` and `RUN_SUMMARY`. `COMPLETE` means the requested work completed; `PARTIAL` reports preserved data with a cap, limited coverage, or failed pages; `VALID_EMPTY` covers genuine zero matches and unchanged monitors. `INVALID_INPUT` includes an actionable input correction. `UPSTREAM_FAILED` and `CONFIG_ERROR` end as failed runs when no useful data exists. Check these records before retrying a failed request or reconciling monitor state.

### Builder's note

In my testing, the public pages exposed both annual job compensation and monthly or lump-sum internship stipends. Keeping the original text alongside numeric bounds avoids silently comparing unlike pay periods. I designed monitor fingerprints to omit relative posting age, so a posting becoming “two days ago” does not by itself create another billable change.

For jobs on employer-hosted boards, continue with [Ashby Jobs Scraper](https://apify.com/khadinakbar/ashby-jobs-scraper). It reads public Ashby boards for broader hiring research alongside the Internshala feed.

### Public data and responsible use

This is an independent tool and is not affiliated with Internshala. It reads public job and internship pages without account login, collects no applicant records, and submits no applications. Users are responsible for reviewing Internshala's terms and applicable data-use requirements for their purpose. Keep copies only as long as needed and verify opportunities on the original posting pages before acting on them.

### Hiring insights and precise change tracking

Enable full details for public applicant counts, applicants per opening, employer hiring history and the employer's published website. Annual salaries are divided by twelve for monthly pay comparison; monthly stipends remain unchanged and other periods remain unconverted. Published deadlines include days remaining and an open/closing-soon/closed status at capture. These are derived fields with explicit rules, not inferred pay or hiring forecasts.

Monitor results include changedFields and previousValues for tracked fields, applicantCountDelta and monthlyCompensationMinDelta. Relative posting age and derived deadline age are excluded from change fingerprints, so another day passing does not itself create a billable change. Additional filters cover part time, internship job offers, early applicant status, company names, required skills, excluded keywords and minimum monthly-equivalent compensation. These filters apply to scanned records.

# Changelog

This Actor's version history is a separate document: https://apify.com/khadinakbar/internshala-jobs-internships-monitor/changelog.md

# Actor input Schema

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

Choose a snapshot scrape or incremental monitor. Scrape returns all matching postings within the caps. Monitor emits new and changed postings against a persistent baseline. Keep watchId and filters stable between monitoring runs.

## `listingType` (type: `string`):

Choose jobs, internships, or both. Both shares the result budget between boards. Each record identifies its own listing type. A maxResults of one can return only one type.

## `searchQuery` (type: `string`):

Search public Internshala keyword result pages. Use a role or skill such as python or digital marketing. Internshala controls keyword matching. Explicit startUrls take precedence over this setting.

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

Supply up to ten public Internshala search or singular detail URLs. Explicit URLs replace generated keyword searches. HTTPS URLs without query parameters are accepted. Account, application, API and plural details routes are rejected.

## `location` (type: `string`):

Filter published location names locally. Matching ignores case and checks whether any location contains this text. Remote-only postings can have no matching city. Coverage is limited to the pages scanned.

## `workFromHome` (type: `boolean`):

Keep listings marked work from home. This is a local filter over scanned listings. Hybrid listings can also carry this flag if their posting URL labels them as work from home. Set false to include all work arrangements.

## `includeDetails` (type: `boolean`):

Fetch each matching public posting for its complete description. Details include published deadlines, openings, skills, perks and eligibility. This adds one request per candidate and runs more slowly. Failed detail pages are reported and never billed as full results.

## `maxResults` (type: `integer`):

Cap returned and billable listings at this number. Allowed values are 1 to 500. Both boards share this cap. Read the current Pricing tab and set an Apify run spending cap to bound event charges.

## `maxPages` (type: `integer`):

Scan at most this many pages per search URL. Allowed values are 1 to 15. Local filters and unchanged monitoring records can reduce returned counts. A page-limited scan cannot establish that a listing has closed.

## `minCompensation` (type: `number`):

Keep listings whose published lower compensation bound meets this amount. Zero disables the filter. Job amounts are usually annual and internship amounts may be monthly or lump sum. This filter compares original amounts without converting periods.

## `watchId` (type: `string`):

Name a persistent monitor baseline. Repeated monitoring runs with identical watchId and filters reuse actor-owned storage in your Apify account. Different filters create separate baselines automatically. Run one occurrence of a watch at a time.

## `stateStoreId` (type: `string`):

Select an existing key-value store to hold monitoring state. Leaving this blank creates or reuses an actor-owned store scoped to your account and watch. The Actor requests read and write access only to the selected resource. Avoid running multiple overlapping occurrences of the same watch.

## `emitInitial` (type: `boolean`):

Emit first-seen postings when a watch begins. Set false to establish a quiet initial baseline without listing charges. Subsequent runs emit only new or materially changed postings. Baselines contain fingerprints and timestamps rather than full posting descriptions.

## `partTime` (type: `boolean`):

Keep listings carrying this published hiring signal. This is a local filter over scanned records. Increase maxPages when matching listings are sparse. False includes all listings.

## `withJobOffer` (type: `boolean`):

Keep listings carrying this published hiring signal. This is a local filter over scanned records. Increase maxPages when matching listings are sparse. False includes all listings.

## `earlyApplicant` (type: `boolean`):

Keep listings carrying this published hiring signal. This is a local filter over scanned records. Increase maxPages when matching listings are sparse. False includes all listings.

## `companyContains` (type: `string`):

Keep company names containing this text. Matching ignores case. This is a local filter over scanned postings. Empty text disables this filter.

## `requiredSkills` (type: `array`):

All requested skills must appear in the published skill list. Matching ignores case. Supply at most twenty terms. Coverage is limited to scanned pages.

## `excludeKeywords` (type: `array`):

Exclude listings when title, company name or description contains any supplied term. Matching ignores case. Supply at most twenty terms. Coverage is limited to scanned pages.

## `minMonthlyCompensation` (type: `number`):

Filter by monthly-equivalent published compensation. Annual amounts are divided by twelve and monthly amounts are unchanged. Weekly, daily, lump-sum and unknown periods remain unconverted and do not match a positive threshold. Zero disables the filter.

## Actor input object example

```json
{
  "mode": "scrape",
  "listingType": "both",
  "searchQuery": "python",
  "startUrls": [],
  "location": "Bangalore",
  "workFromHome": false,
  "includeDetails": false,
  "maxResults": 20,
  "maxPages": 2,
  "minCompensation": 0,
  "watchId": "default",
  "emitInitial": true,
  "partTime": false,
  "withJobOffer": false,
  "earlyApplicant": false,
  "companyContains": "",
  "requiredSkills": [],
  "excludeKeywords": [],
  "minMonthlyCompensation": 0
}
```

# Actor output Schema

## `listings` (type: `string`):

Validated jobs, internships and monitor changes.

## `output` (type: `string`):

Truthful terminal outcome, counts and event totals.

## `runSummary` (type: `string`):

Coverage, failures and native request diagnostics.

## `snapshot` (type: `string`):

Fingerprint-only current watch state when monitoring.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/internshala-jobs-internships-monitor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/internshala-jobs-internships-monitor").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 '{}' |
apify call khadinakbar/internshala-jobs-internships-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/internshala-jobs-internships-monitor"
        }
    }
}
```

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/D3ingc9E1oymC5k8o/builds/ZBmQtp1zNgf9ky6O9/openapi.json
