# TopJobs.lk Scraper (`zucchini_gopher_m2v/topjobs-lk-scraper`) Actor

Search Sri Lanka's leading job board, TopJobs.lk, by keyword or category and pull full job details (location, closing date, job-type badges, description image, employer contact) -- no account or API key needed.

- **URL**: https://apify.com/zucchini\_gopher\_m2v/topjobs-lk-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/zucchini_gopher_m2v) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 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.
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?

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

## TopJobs.lk Scraper

Search Sri Lanka's leading job board, TopJobs.lk, by keyword or category and get back full job details — location, closing date, job-type badges, description image, and employer contact info.

### Why use this actor

- No account, login, or API key required
- Search by free-text keyword or browse any of the site's 30 job categories
- Every result can be hydrated into a full job record with one extra request per job
- Automatic pagination up to the number of results you ask for
- Also works the other way: give it specific job links and it fetches full detail for exactly those

### How it works

Pick a `mode`:

1. **`search`** (default) — searches job titles by keyword, or browses one category, and returns full job details for each match, with pagination handled automatically.
2. **`detail`** — fetches full detail for one or more specific job URLs you already have.

You don't need to manage any browsers or automatic retries — just provide a keyword or a list of job links.

**Important:** on TopJobs.lk's own site, category browsing and keyword search are mutually exclusive — if you set `functionalArea`, `keyword` is ignored. Filter checkboxes (`government`, `ngo`, `partTime`, `walkIn`, `foreign`, `internship`) combine fine with `keyword`, just not with `functionalArea`.

### Input

**Keyword search (default mode):**

```json
{
  "mode": "search",
  "keyword": "accountant",
  "maxResults": 50,
  "fetchFullDetails": true,
  "maxConcurrency": 4
}
```

**Browse a category:**

```json
{
  "mode": "search",
  "functionalArea": "SDQ",
  "maxResults": 50
}
```

**Job detail by URL:**

```json
{
  "mode": "detail",
  "jobUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541016&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp"
}
```

| Field | Type | Description |
|---|---|---|
| `mode` | string | `"search"` (default) or `"detail"`. |
| `keyword` | string | `search` mode — free-text search (e.g. `"accountant"`, `"software engineer"`). Leave empty to list all currently open vacancies. Ignored if `functionalArea` is set. |
| `functionalArea` | string | `search` mode — browse one of 30 job categories instead of a keyword search (e.g. `SDQ` = IT-Software, `ACA` = Accounting/Auditing/Finance). Overrides `keyword` when set. |
| `government` / `ngo` / `partTime` / `walkIn` / `foreign` / `internship` | boolean | `search` mode — optional filter checkboxes, combine with `keyword` (not with `functionalArea`). |
| `maxResults` | integer | `search` mode — max jobs to return, pagination followed automatically (1000/page). Default 50. |
| `fetchFullDetails` | boolean | `search` mode — when on (default), each result includes full location/closing-date/job-type/description-image/contact data (one extra request per job). Turn off for a faster run that only needs title, employer, town, and dates. |
| `jobUrl` / `jobUrls` | string / array | `detail` mode — a job's full topjobs.lk detail URL (from this actor's own `detailUrl` search-result field, or copied from the site). |
| `maxConcurrency` | integer | How many detail requests run in parallel. Default 4. |
| `proxyConfiguration` | object | Apify Proxy settings. Residential is on by default. |

### Output

**Search mode (`fetchFullDetails: true`, default)** — real output:

```json
{
  "_input": "keyword:accountant",
  "_source": "S1-detail-html",
  "_scrapedAt": "2026-08-26T03:11:27Z",
  "position": "Assistant Accountant / Senior Finance Executive / Finance Executive",
  "employer": "Sentab Groups",
  "location": "Piliyandala, Sri Lanka",
  "jobRefNo": "1541016",
  "closingDate": "08/09/2026",
  "jobType": ["Full Time", "Permanent", "Onsite"],
  "descriptionImageUrl": "https://topjobs.lk/logo/DEFZZZ/6044cKKKHH.png",
  "companyLogoUrl": "https://topjobs.lk/logo/defzzz_small.gif",
  "companyWebsite": null,
  "companyEmail": "careers@sentab.lk",
  "jobSpecCode": "0001541016",
  "jsonLd": {
    "@context": "http://schema.org/",
    "@type": "JobPosting",
    "description": "Please refer the full details of the job posting as given by the employer. </br>...",
    "title": "Assistant Accountant / Senior Finance Executive / Finance Executive",
    "validThrough": "2026-09-08T00:00:00+0530",
    "datePosted": "2026-08-26",
    "hiringOrganization": { "@type": "Organization", "name": "Sentab Groups" },
    "jobLocation": [{ "@type": "Place", "address": { "@type": "PostalAddress", "addressLocality": "Piliyandala", "addressCountry": "LK", "addressRegion": "Western" } }]
  },
  "jc": "0001541016",
  "ac": "DEFZZZ",
  "ec": "DEFZZZ",
  "detailUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541016&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp"
}
```

**Search mode (`fetchFullDetails: false`)** — compact result, no extra request per job:

```json
{
  "_input": "keyword:sales",
  "_source": "S1-search-html",
  "_scrapedAt": "2026-08-26T03:11:30Z",
  "jobRefNo": "1541173",
  "position": "Sales Associate",
  "employer": "L.K.Group Holdings Pvt Ltd",
  "town": "Nugegoda",
  "openingDate": "Wed Aug 26 2026",
  "closingDate": "Wed Sep 09 2026",
  "jc": "0001541173",
  "ac": "DEFZZZ",
  "ec": "DEFZZZ",
  "detailUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541173&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp"
}
```

| Field | Type | Description |
|---|---|---|
| `position` | string | Job title. |
| `employer` | string | Employer name — may read as "Company Name Withheld" when the employer chose to stay anonymous. |
| `town` / `location` | string | `town` on compact search records, `location` (town + country) on full-detail records. |
| `closingDate` / `openingDate` | string | Application deadline / listing date, as displayed on site (format varies by record type). |
| `jobType` | array | Full-detail records only — 0-3 dynamic badges, e.g. `["Full Time", "Permanent", "Onsite"]` or `["Part Time"]`. |
| `descriptionImageUrl` | string | Full-detail records only — see **Known limits**: TopJobs.lk publishes the actual job description as an image, not text. |
| `companyEmail` / `companyWebsite` | string | Full-detail records only — employer's public contact info, when supplied. |
| `jsonLd` | object | Full-detail records only, and only present on roughly half of jobs (a site quirk) — the raw schema.org JobPosting block. |
| `detailUrl` | string | Canonical job URL — feed this back into `detail` mode to re-fetch it directly. |

A record with `_error` instead means that lookup couldn't complete (e.g. `"_error": "not_found"` for an expired/invalid job URL).

### Known limits

- **No extractable plain-text job description.** TopJobs.lk employers upload a designed ad graphic instead of filling in a text field — every job sampled during development had its actual requirements/responsibilities rendered as an image (`descriptionImageUrl`), not text. This is the site's real publishing model, not a gap in this actor.
- **Category browsing (`functionalArea`) and `keyword` can't be combined** — a site-side limitation, confirmed live. Use one or the other; filter checkboxes work with `keyword`.
- **`detail` mode needs the full job URL**, not just a reference number — there's no shorter `job/<id>` shortcut on the site itself. Always use this actor's own `detailUrl` output field, or copy the URL directly from TopJobs.lk.

# Actor input Schema

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

"search" runs a keyword or category search and (by default) hydrates each result into a full job record. "detail" fetches full detail for specific job URL(s) directly.

## `keyword` (type: `string`):

"search" mode -- free-text search (e.g. "accountant", "software engineer"). Leave empty to list all currently open vacancies. Ignored if "Functional area" below is set -- topjobs.lk's own search page treats category-browse and keyword-search as mutually exclusive, category wins.

## `functionalArea` (type: `string`):

"search" mode -- browse one of topjobs.lk's own job categories instead of a keyword search. When set, "Keyword" above is ignored server-side (confirmed live).

## `government` (type: `boolean`):

"search" mode -- optional filter checkbox, combines with Keyword (does NOT combine with Functional area).

## `ngo` (type: `boolean`):

"search" mode -- optional filter checkbox, combines with Keyword.

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

"search" mode -- optional filter checkbox, combines with Keyword.

## `walkIn` (type: `boolean`):

"search" mode -- optional filter checkbox, combines with Keyword.

## `foreign` (type: `boolean`):

"search" mode -- optional filter checkbox, combines with Keyword.

## `internship` (type: `boolean`):

"search" mode -- optional filter checkbox, combines with Keyword.

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

"search" mode -- maximum number of job listings to return. Pagination is followed automatically (1000 results/page) until this cap or the end of the result set.

## `fetchFullDetails` (type: `boolean`):

"search" mode -- when on (default), each search result is hydrated into a full job record (location, closing date, job-type badges, description image, employer contact) with one extra request per job. Turn off for faster/cheaper runs that only need the compact search-result fields (title, employer, town, dates).

## `jobUrl` (type: `string`):

"detail" mode -- a single job's full topjobs.lk detail URL (from this actor's own `detailUrl` search-result field, or copied from the site).

## `jobUrls` (type: `array`):

"detail" mode only -- multiple job detail URLs to fetch full detail for in one run.

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

How many detail requests to run in parallel (used in "search" mode when hydrating results, and in "detail" mode across multiple URLs).

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

Apify Proxy configuration. Residential is on by default.

## Actor input object example

```json
{
  "mode": "search",
  "keyword": "accountant",
  "functionalArea": "",
  "government": false,
  "ngo": false,
  "partTime": false,
  "walkIn": false,
  "foreign": false,
  "internship": false,
  "maxResults": 50,
  "fetchFullDetails": true,
  "jobUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541024&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp",
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All scraped job search / detail records produced by this run.

# 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 = {
    "mode": "search",
    "keyword": "accountant",
    "functionalArea": "",
    "maxResults": 50,
    "fetchFullDetails": true,
    "jobUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541024&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp",
    "maxConcurrency": 4,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zucchini_gopher_m2v/topjobs-lk-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 = {
    "mode": "search",
    "keyword": "accountant",
    "functionalArea": "",
    "maxResults": 50,
    "fetchFullDetails": True,
    "jobUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541024&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp",
    "maxConcurrency": 4,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("zucchini_gopher_m2v/topjobs-lk-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 '{
  "mode": "search",
  "keyword": "accountant",
  "functionalArea": "",
  "maxResults": 50,
  "fetchFullDetails": true,
  "jobUrl": "https://topjobs.lk/employer/JobAdvertismentServlet?rid=0&ac=DEFZZZ&jc=0001541024&ec=DEFZZZ&pg=applicant/vacancybyfunctionalarea.jsp",
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call zucchini_gopher_m2v/topjobs-lk-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zucchini_gopher_m2v/topjobs-lk-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/2rzHTLDhQgXseNnyW/builds/cbBnZE4Tyas3bUB1S/openapi.json
