# Internshala Scraper (`mlg14/internshala-scraper`) Actor

Collect public Internshala internships and fresher jobs with stipends, salaries, skills, company details, and descriptions.

- **URL**: https://apify.com/mlg14/internshala-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Jobs, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.32 / 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/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

## Internshala Scraper

Scrape Internshala internships and jobs, then export the public listings to CSV, JSON, or Excel. This Internshala API alternative collects titles, locations, organizations, pay ranges, skills, descriptions, and application deadlines from searches you choose.

Search by keyword, category, or city, or paste an existing public search URL. A listing-only run is faster; detail enrichment adds the full description, applicant requirements, openings, perks, and the public organization overview when those details appear on the listing page. Each row includes a stable listing ID and a link back to its source.

### What data can you extract from Internshala?

Each dataset row represents one internship or job. The actor returns the same field names on every row. A null value means that the public page did not provide that value or that detail enrichment was disabled. Empty arrays mean no values were found in that section. The pay fields retain the period shown by the source, so a weekly stipend is labeled WEEK and an annual salary is labeled YEAR.

| Field | Description | Example |
|---|---|---|
| `jobId` | Stable public listing identifier | `"3296830"` |
| `jobKey` | Public listing key for matching exports | `"3296830"` |
| `listingType` | Internship or job | `"internship"` |
| `title` | Role title | `"Makeup Artist & Stylist"` |
| `company` | Hiring organization name | Public listing value |
| `companyLogo` | Public logo URL, if shown | URL or null |
| `companyUrl` | Public organization profile | URL or null |
| `location` | Location text as displayed | `"Bangalore"` |
| `isWorkFromHome` | Whether the listing indicates remote work | `false` |
| `isPartTime` | Whether the listing indicates part-time work | `false` |
| `workMode` | Remote or onsite classification | `"onsite"` |
| `category` | Industry or role category when structured | `"Entertainment"` |
| `skills` | Public skill tags | `["Effective Communication"]` |
| `description` | Description text, complete after enrichment | Text or null |
| `descriptionHtml` | Description markup from the detail page | HTML or null |
| `descriptionLength` | Character count of the description text | Integer or null |
| `whoCanApply` | Applicant eligibility text | Text or null |
| `otherRequirements` | Additional eligibility requirements | Text or null |
| `perks` | Public perk labels | `[]` |
| `aboutCompany` | Public organization overview | Text or null |
| `stipendText` | Original pay text | `"₹ 6,000 - 15,000 /month"` |
| `salaryMin` | Lower pay bound | `6000` |
| `salaryMax` | Upper pay bound | `15000` |
| `salaryCurrency` | Pay currency | `"INR"` |
| `salaryPeriod` | MONTH, WEEK, or YEAR when stated | `"MONTH"` |
| `employmentType` | Structured employment type | `"INTERN, FULL_TIME"` |
| `duration` | Duration label | `"6 Months"` |
| `durationMonths` | Numeric months when available | `6` |
| `startDate` | Start label or date | `"Immediately"` |
| `numberOfOpenings` | Published opening count | `7` |
| `hasJobOffer` | Job-offer signal on the listing card | `false` |
| `activelyHiring` | Hiring badge on the card | `true` |
| `earlyApplicant` | Early-applicant badge on the card | `true` |
| `postedAt` | Structured posting date | `"2026-09-22"` |
| `publishedAge` | Relative age displayed on the card | `"4 days ago"` |
| `applyBy` | Application deadline label | `"22 Oct' 26"` |
| `validThrough` | Structured expiry timestamp | `"2026-10-22 23:59:59"` |
| `canonicalUrl` | Direct public listing URL | URL |
| `applyUrl` | Public application landing page | URL |
| `sourceUrl` | Detail page used for the record | URL |
| `sourceCountry` | Country code of the source market | `"IN"` |
| `sourceDomain` | Source website domain | `"internshala.com"` |
| `searchUrl` | Search page that found the record | `"https://internshala.com/internships/"` |
| `searchQuery` | Search path segment or all | `"all"` |
| `scrapedAt` | UTC timestamp of collection | ISO timestamp |
| `fetchedAt` | UTC timestamp after item collection | ISO timestamp |
| `detailFetched` | Whether detail parsing succeeded | `true` |
| `contentQuality` | Full detail or listing summary | `"full"` |
| `contentHash` | Digest of title and description | Hexadecimal digest |

The three listing URLs currently lead to the same public detail page. Keep `canonicalUrl` for matching records across exports and `searchUrl` to preserve where a record was discovered. The public application page may ask the visitor to sign in before submitting an application; the actor does not submit applications or access private applicant information.

### How to scrape Internshala

1. Choose **Internships** or **Jobs** in `listingType`.
2. Enter a keyword, choose a category and city, or paste one or more public search pages into `startUrls`.
3. Set `maxResults` and `maxPages`. Leave `includeDetails` on when you need full descriptions and requirements.
4. Run the actor and inspect the default dataset. Download JSON, CSV, or Excel in the dataset view.

Start with a small limit to check the relevance of the search. A maximum is a stopping point rather than a promised result count: narrow searches may have fewer public listings than requested. For a regular snapshot, save the exact input so future runs use the same search scope. Compare rows by `jobId` or `canonicalUrl` when combining snapshots.

### Input

All fields are optional. Without a query, filter, or start URL, the actor begins at the broad public internship search. A keyword creates its own search source. Category and location combine into a second source; the remote and part-time switches add their corresponding search pages. Results from all sources are merged and deduplicated by listing ID.

| Name | Type | Default | Description |
|---|---|---|---|
| `listingType` | string | `"internship"` | Select internships or jobs. |
| `query` | string | Empty | A keyword phrase such as web development. |
| `category` | string | Empty | Public category slug such as web-development. |
| `location` | string | Empty | City slug such as mumbai. |
| `workFromHome` | boolean | `false` | Add the remote search page as another source. |
| `partTime` | boolean | `false` | Add the part-time search page as another source. |
| `startUrls` | string array | Empty | Additional public search URLs under /internships/ or /jobs/. |
| `maxResults` | integer | `100` | Stop after this many unique rows; zero removes the cap. |
| `maxPages` | integer | `5` | Stop after this many pages per search source. |
| `includeDetails` | boolean | `true` | Fetch detail pages for full descriptions and other fields. |
| `minStipend` | integer | `0` | Minimum monthly internship stipend in INR. |
| `minSalary` | integer | `0` | Minimum annual job salary in INR. |
| `proxyConfiguration` | object | Connection enabled | Optional connection settings. |

For example, this input takes up to 30 internships from the broad public listing and fetches each detail page. It is the input used for the 30-record verification run:

```json
{
  "listingType": "internship",
  "maxResults": 30,
  "maxPages": 2,
  "includeDetails": true
}
```

For a focused city and category search, use `{"category":"web-development","location":"mumbai","maxResults":100}`. For a keyword search, enter `{"query":"web development","maxResults":100}`. These search forms can surface different sets of listings. If you need exact filters that are already available on a public search page, paste that page into `startUrls` and keep the result limit explicit.

Numeric stipend and salary thresholds apply after a listing has been read. Listings without a numeric pay amount remain in the output because the amount cannot be compared. Weekly internship pay is converted to an approximate monthly amount using 52 weeks divided by 12 months for the threshold check; the stored `salaryMin`, `salaryMax`, and `salaryPeriod` still reflect the source's weekly terms. Review `stipendText` whenever compensation matters.

### Output example

These selected fields came from one record in a successful 30-item verification run. The hiring organization and its identifying URL are omitted from this illustration. The dataset retains all documented fields and the complete public description.

```json
{
  "jobId": "3296830",
  "jobKey": "3296830",
  "listingType": "internship",
  "title": "Makeup Artist & Stylist",
  "location": "Bangalore",
  "isWorkFromHome": false,
  "isPartTime": false,
  "workMode": "onsite",
  "category": "Entertainment",
  "skills": ["Effective Communication"],
  "stipendText": "₹ 6,000 - 15,000 /month",
  "salaryMin": 6000,
  "salaryMax": 15000,
  "salaryCurrency": "INR",
  "salaryPeriod": "MONTH",
  "numberOfOpenings": 7,
  "activelyHiring": true,
  "earlyApplicant": true,
  "postedAt": "2026-09-22",
  "applyBy": "22 Oct' 26",
  "searchUrl": "https://internshala.com/internships/",
  "detailFetched": true
}
```

The verification run returned 30 items. All 30 had IDs, titles, organization values, locations, descriptions, and direct listing URLs, and all 30 detail pages were parsed. Those observations describe that run, not a permanent fill-rate guarantee. Job searches and niche filters can expose different fields.

### Use cases

- Career researchers can compare public internship openings by city, title, duration, and required skills.
- Students can create a bounded list of currently visible opportunities and keep each source link for review.
- Compensation analysts can study published stipend or salary ranges while preserving the original pay period.
- Placement teams can track public opening counts and application deadlines across categories.
- Market researchers can compare demand for skill tags in different cities or between internships and jobs.
- Reporting teams can refresh a saved search on a schedule and compare stable listing IDs across runs.

These workflows are strongest when the input scope stays consistent. A keyword search and a category search are different public result views, even if they share the same words. When comparing runs, record the search input and run time alongside the exported dataset. Use the source URL to verify any listing that will drive an individual decision.

### How much does it cost to scrape Internshala?

The configured result-event price is **$0.32 per 1,000 output rows**, or $0.00032 per result. At that rate, 30 rows produce $0.0096 in result events, 100 rows produce $0.032, and 500 rows produce $0.16. A 5,000-row illustration would produce $1.60 in result events, but a particular public search may expose fewer rows. Platform resources such as processing and network use can add to the result-event charge; check the run's usage for the actual total.

The limit controls how many records are delivered and charged as results. Detail enrichment fetches one extra public page per candidate row, so its resource usage and run time can be higher than a listing-only search. A narrow query with 20 public results will finish below a `maxResults` value of 100. Multiple search sources can overlap; duplicate listing IDs are emitted once.

### Tips for best results

Use `query` when the phrase itself is the search intent. Use `category` and `location` when you want a recognized category in a particular city. Use a copied public search URL when the site's own filters express your target more precisely. A saved URL is useful for repeating a specific view, although the site may change its URL format over time.

Set `maxPages` high enough to reach the number of listings you need. Public result pages typically show up to 50 cards, but some filtered or later pages show fewer. The actor follows the search endpoint's last-page signal and stops early when a page is empty. A large `maxResults` with a small page limit can finish below the result cap.

Keep `includeDetails` enabled for applicant requirements, perks, openings, company overview, and the complete description. Disable it for faster collection when titles, locations, pay text, short descriptions, and visible skill tags are sufficient. The `detailFetched` flag lets downstream processing distinguish a complete detail parse from a listing-only row or a detail request that failed.

When joining exports, use `jobId` as the primary key. The same listing can appear in several public search views, and the actor removes duplicates within one run. Across separate runs, a listing can change text or disappear. To monitor those changes, compare snapshots in your own storage using the ID and `scrapedAt`; the actor itself does not maintain change state between runs.

### Limits

Only publicly visible listings are collected. The actor cannot access an application form that requires an account, private employer contact data, or a removed listing. A public result count may be larger than the number of pages available to a given search view. The actor stops at `maxPages`, at the site's last-page signal, or at `maxResults`, whichever comes first.

Internship detail pages do not all expose the same structured fields. `postedAt`, `validThrough`, `category`, `salaryPeriod`, and organization information can be null even when the listing is valid. A visible relative label such as “4 days ago” is preserved as `publishedAge` rather than converted into a guessed calendar date. Stipends can be monthly, weekly, or undisclosed. The original text remains in `stipendText`.

The source can change its HTML structure, search URL rules, or anti-abuse behavior. A successful run indicates that the public data path worked at that time. When a record is central to a decision, open `canonicalUrl` and confirm that it is still current. The actor does not promise a complete historical archive or a specific number of listings for a search.

### Use with automated clients

The same JSON input can be sent through an automated client that starts a run and reads the dataset. Ask for a bounded search, preserve the listing IDs, and include source links in the downstream record. Two practical requests are: “Collect up to 50 public web-development internships in Mumbai with their stipends and application deadlines,” and “Collect 100 public remote jobs, then group the stated salary ranges by pay period.” The results should be treated as source data, with null fields left unknown.

For repeated collection, save the input as a job configuration and download each run's dataset. A client can compare IDs to identify newly visible listings and compare selected fields to find changed postings. This comparison is performed outside the actor; each actor run produces a current snapshot of its chosen public searches.

### FAQ

**Is it legal to collect these listings?** This actor reads publicly available listing pages. Review the source site's terms and applicable privacy law for your use. Do not misuse personal information, submit applications without authorization, or treat a public page as permission to contact someone.

**Do I need to configure a connection?** No. The default configuration is provided. Advanced users can change `proxyConfiguration` if their environment requires it.

**How long does a run take?** It depends on the number of search pages and whether detail enrichment is enabled. A 30-item enriched verification run completed successfully; larger runs make more requests. Start with a bounded run to measure the time for your chosen scope.

**Can I schedule recurring collection?** Yes. Schedule the same input and retain each dataset as a snapshot. Compare IDs or URLs across snapshots to identify additions and removals.

**Can I export to spreadsheets?** Yes. Download CSV or Excel from the dataset. JSON preserves arrays, numbers, booleans, and nulls more precisely for programmatic processing.

**Why is a field empty?** The public listing may not expose it, the detail page may have been unavailable, or `includeDetails` may be off. Check `detailFetched` and the source URL before treating a missing value as a negative fact.

**Can I combine several public searches?** Yes. Provide keyword, category, city, remote or part-time sources, and additional `startUrls`. The actor merges their results and emits each listing ID once.

### Integrations

The default dataset can be retrieved through the platform API, downloaded manually, or sent to an external workflow after a run finishes. Webhooks and scheduling can support periodic updates; CSV and Excel exports suit spreadsheet reporting. Keep the source URL and stable ID when moving rows into another system so a later snapshot can be matched to the earlier one.

### Support

Open an issue on the Issues tab with the input, run ID, and an example listing URL. Include which field was missing or incorrect so the source view can be checked.

# Actor input Schema

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

Choose internships or jobs.

## `query` (type: `string`):

Keyword phrase such as web development. Keyword searches are separate from category and location searches.

## `category` (type: `string`):

Public category slug, such as web-development. Combine with location to search that category in a city.

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

City slug, such as mumbai or bangalore. Combine with category for a narrower search.

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

Add the site's work-from-home listing search as another source.

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

Add the site's part-time listing search as another source.

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

Public search URLs under /internships/ or /jobs/ to add to the run. Results are deduplicated by listing ID.

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

Maximum listings in the output. Zero means no result cap.

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

Maximum search result pages fetched for each source.

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

Fetch each listing page for full description, skills, openings, application deadline, and company overview.

## `minStipend` (type: `integer`):

Keep internships whose listed maximum stipend reaches this INR amount; listings without a numeric stipend remain.

## `minSalary` (type: `integer`):

Keep jobs whose listed maximum annual salary reaches this INR amount; listings without a numeric salary remain.

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

Connection configuration for public pages.

## Actor input object example

```json
{
  "listingType": "internship",
  "query": "web development",
  "workFromHome": false,
  "partTime": false,
  "maxResults": 100,
  "maxPages": 5,
  "includeDetails": true,
  "minStipend": 0,
  "minSalary": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped internships and jobs in the default dataset.

# 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 = {
    "query": "web development",
    "maxResults": 100,
    "includeDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/internshala-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 = {
    "query": "web development",
    "maxResults": 100,
    "includeDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/internshala-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 '{
  "query": "web development",
  "maxResults": 100,
  "includeDetails": true
}' |
apify call mlg14/internshala-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/internshala-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/eFqwwB93S0KWhDHFe/builds/7FluXpnjGLQlko5KD/openapi.json
