# JustJoin.it Scraper — IT Jobs, Salaries & Tech Stacks (`haketa/justjoin-it-scraper`) Actor

Scrape IT & tech jobs from JustJoin.it, Poland's #1 developer job board. Export titles, transparent salaries in PLN/EUR/USD/GBP, required tech stack with skill levels, seniority, work model, city, coordinates & company data. Filter by 25 categories, city & experience. JSON, CSV, Excel.

- **URL**: https://apify.com/haketa/justjoin-it-scraper.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Jobs, Developer tools
- **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/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

## JustJoin.it Scraper — IT Jobs, Salaries & Tech Stacks 🇵🇱💻

![Jobs](https://img.shields.io/badge/dataset-IT%20jobs-2563eb)
![Salaries](https://img.shields.io/badge/salary-PLN%20%7C%20EUR%20%7C%20USD%20%7C%20GBP-16a34a)
![Tech stack](https://img.shields.io/badge/tech%20stack-required%20%2B%20nice--to--have-7c3aed)
![Poland](https://img.shields.io/badge/market-Poland%20%2B%20remote-e11d48)
![Format](https://img.shields.io/badge/output-JSON%20%7C%20CSV%20%7C%20Excel-f59e0b)
![No login](https://img.shields.io/badge/login-not%20required-0891b2)
![Pay per result](https://img.shields.io/badge/pricing-pay%20per%20result-0d9488)
![Maintained](https://img.shields.io/badge/status-actively%20maintained-22c55e)
![Support](https://img.shields.io/badge/support-responsive-3b82f6)

Turn **JustJoin.it** — Poland's #1 IT & tech job board — into clean, structured data. Get **transparent salary ranges in four currencies**, the **exact required tech stack with proficiency levels**, seniority, work model, location, coordinates and company details for every offer. Perfect for **recruiters, market-research, salary benchmarking, dev-tool lead generation and job-aggregators**.

> No account, cookies or manual setup needed. Pick a category (or paste a search URL), press **Start**, and export to JSON, CSV, Excel, or push straight into your app via API.

***

### ✨ Why this scraper

- 💸 **Real salary data, four currencies** — most job scrapers return empty salary fields. This one gives you the **from–to range in PLN, EUR, USD, GBP and CHF**, plus contract type (B2B / permanent), period (month / hour) and gross/net flag.
- 🧩 **Full tech stack, with levels** — optional detail mode returns **required** and **nice-to-have** skills, each with a 1–5 proficiency level (e.g. `React · 4/5`, `AWS · 3/5`). Ideal for demand analysis and skill-gap research.
- 🎯 **Precise filtering** — filter by any of **25 IT categories**, seniority (Junior / Mid / Senior), working time, city, or a free-text keyword.
- 📍 **Location + geo** — city, street, latitude & longitude for every posting (including multi-location offers).
- 🏢 **Company insight** — company name, logo, size, website and the direct **apply URL**.
- ⚡ **Fast & reliable** — pure data extraction, no browser overhead. Deduplicated automatically (0 duplicates).
- 🧾 **Structured & clean** — flat, predictable JSON with stable columns, ready for spreadsheets, BI tools and databases.

***

### 📦 What you get per offer

| Field | Description |
|---|---|
| `title` | Job title |
| `companyName`, `companyLogo` | Employer name & logo |
| `category`, `categoryId` | IT category (e.g. JavaScript, Python, DevOps) |
| `experienceLevel` | junior / mid / senior |
| `workplaceType` | remote / hybrid / office |
| `workingTime` | full\_time / part\_time / freelance |
| `salaryText` | Human-readable salary (e.g. `18 000 - 24 000 PLN / month (b2b net)`) |
| `minSalaryPln`, `maxSalaryPln` | Normalized salary range in PLN (great for sorting) |
| `employmentTypes[]` | Full salary breakdown: type, unit, gross, and from/to in **PLN, EUR, USD, GBP, CHF** |
| `city`, `street`, `latitude`, `longitude` | Location & coordinates |
| `multilocation[]` | All office locations for multi-city offers |
| `languages[]` | Required languages with levels (e.g. `en · b2`) |
| `openToHireUkrainians`, `remoteInterview` | Useful hiring flags |
| `publishedAt`, `lastPublishedAt`, `expiredAt` | Timestamps |
| `applyMethod`, `isSuperOffer` | Application method & promoted flag |
| `url` | Direct link to the offer |
| **With `includeDetails`** | |
| `requiredSkills[]`, `niceToHaveSkills[]` | Tech stack with proficiency level (1–5) |
| `companySize`, `companyUrl` | Company size & website |
| `applyUrl` | Direct application link (often the employer's ATS) |
| `description` | Full job description text |

***

### 🚀 Quick start

1. Open the actor and (optionally) pick one or more **Tech categories**.
2. Add any filters you like — **city**, **seniority**, **working time**, or a **keyword**.
3. Set **Maximum offers**.
4. (Optional) Enable **Include full details** to also get the tech stack and description.
5. Click **Start**. Export from the **Storage** tab or fetch via API.

#### Example input

```json
{
  "categories": ["javascript", "python"],
  "city": "Warszawa",
  "experienceLevels": ["senior"],
  "workingTimes": ["full_time"],
  "sortBy": "salary",
  "includeDetails": true,
  "maxItems": 500
}
```

#### Scrape everything (all categories, newest first)

```json
{
  "categories": [],
  "sortBy": "published",
  "maxItems": 1000
}
```

#### Paste a search URL instead

```json
{
  "startUrls": [{ "url": "https://justjoin.it/job-offers/all-locations/python" }],
  "maxItems": 300
}
```

***

### 🗂️ Categories you can filter by

| | | | | |
|---|---|---|---|---|
| JavaScript | Python | Java | .NET | PHP |
| HTML | Ruby | Scala | C / C++ | Go |
| Mobile | Testing | DevOps | Admin | UX/UI |
| PM | Game | Analytics | Security | Data |
| Support | ERP | Architecture | AI/ML | Other |

***

### 📤 Example output

```json
{
  "id": "3b16c83f-3aac-467b-a300-cdb3e89c20d0",
  "url": "https://justjoin.it/job-offer/example-senior-frontend-warszawa",
  "title": "Senior Frontend Developer",
  "companyName": "Acme Software",
  "category": "JavaScript",
  "experienceLevel": "senior",
  "workplaceType": "hybrid",
  "workingTime": "full_time",
  "salaryText": "18 000 - 24 000 PLN / month (b2b net)",
  "minSalaryPln": 18000,
  "maxSalaryPln": 24000,
  "employmentTypes": [
    {
      "type": "b2b", "unit": "month", "gross": false, "currency": "pln",
      "from": 18000, "to": 24000,
      "fromEur": 4180, "toEur": 5570,
      "fromUsd": 4520, "toUsd": 6030
    }
  ],
  "city": "Warszawa",
  "latitude": "52.2328",
  "longitude": "20.9985",
  "languages": [{ "code": "en", "level": "b2" }],
  "requiredSkills": [
    { "name": "React", "level": 4 },
    { "name": "TypeScript", "level": 4 },
    { "name": "GraphQL", "level": 3 }
  ],
  "publishedAt": "2026-08-12T11:19:42.271Z"
}
```

***

### 💡 Popular use cases

- **Recruitment & sourcing** — build a live feed of open IT roles, filtered by stack and seniority.
- **Salary benchmarking** — compare compensation by role, city, seniority and contract type across four currencies.
- **Tech-stack / demand analytics** — measure which frameworks and tools employers ask for, and at what proficiency.
- **Dev-tool & B2B lead generation** — identify companies actively hiring for a given technology.
- **Job boards & aggregators** — keep your listings fresh with structured, deduplicated data.
- **Relocation & remote-work research** — filter remote-friendly and Ukraine-friendly roles.

***

### 🔌 Run via API

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("YOUR_ACTOR_ID").call(run_input={
    "categories": ["python", "data"],
    "experienceLevels": ["senior"],
    "sortBy": "salary",
    "maxItems": 500,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], "—", item.get("salaryText"))
```

**JavaScript / Node.js**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('YOUR_ACTOR_ID').call({
    categories: ['javascript'],
    city: 'Kraków',
    maxItems: 300,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

***

### 🧾 Output formats

Every run can be exported as **JSON, CSV, Excel, HTML or an RSS feed**, or accessed programmatically through the Apify API. Data is stored in the run's dataset and never expires until you delete it.

***

### ❓ FAQ

**Do I need an account or login for JustJoin.it?**
No. The actor works out of the box — just set your filters and run it.

**Are salaries always included?**
Salary is returned whenever the employer publishes it. When present you get the full range in five currencies. The `hasSalary` flag tells you which offers disclose pay.

**How do I get the tech stack for each job?**
Enable **Include full details**. You'll then get `requiredSkills` and `niceToHaveSkills`, each with a proficiency level, plus the full description.

**Can I scrape only remote jobs?**
Yes — filter by category/city as needed and use the `workplaceType` field (`remote` / `hybrid` / `office`) to post-process, or add a keyword.

**How many offers can I collect?**
As many as you want — set `maxItems`. The board holds thousands of active offers at any time.

**Is it fast?**
Yes. Listing mode is very fast; enabling full details adds one request per offer, so it's slower but richer.

***

### ⚖️ Legal & responsible use

This actor collects **publicly available** job-offer information for legitimate purposes such as recruitment, market research and analytics. It does not access private, gated or personal data, and it is **not affiliated with, endorsed by, or connected to JustJoin.it or its operators** — all product and company names are trademarks of their respective owners.

You are responsible for how you use the collected data. Use it in compliance with applicable laws (including the GDPR), the target website's Terms of Service, and any relevant data-protection regulations. Respect rate limits and use the data ethically.

***

#### 💬 Support & feedback

Found a bug, need an extra field or a new filter? Open an issue or drop a message — feature requests are welcome and the actor is **actively maintained**.

# Actor input Schema

## `categories` (type: `array`):

Filter by IT category. Leave empty to scrape every category. You can select more than one.

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

Filter offers by city, e.g. Warszawa, Kraków, Wrocław, Gdańsk. Leave empty for all locations.

## `experienceLevels` (type: `array`):

Filter by experience level. Leave empty for all.

## `workingTimes` (type: `array`):

Filter by working time. Leave empty for all.

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

Optional. Keep only offers whose job title or company name contains this text (e.g. React, Kubernetes, AWS).

## `sortBy` (type: `string`):

Order of the results.

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

Fetch each offer page to add the required & nice-to-have tech stack (with proficiency level), company size, apply URL and full job description. Slower and more expensive — turn off if you only need the listing data.

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

Optionally paste JustJoin.it search page URLs. Category and city are auto-detected from the URL path and merged with the filters above.

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

Maximum number of job offers to collect across all filters.

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

Proxy settings. Optional for small runs; recommended for large ones to avoid rate limiting.

## Actor input object example

```json
{
  "categories": [],
  "city": "Warszawa",
  "experienceLevels": [],
  "workingTimes": [],
  "sortBy": "published",
  "includeDetails": false,
  "startUrls": [],
  "maxItems": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `id` (type: `string`):

No description

## `url` (type: `string`):

No description

## `title` (type: `string`):

No description

## `companyName` (type: `string`):

No description

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

No description

## `experienceLevel` (type: `string`):

No description

## `workplaceType` (type: `string`):

No description

## `salaryText` (type: `string`):

No description

## `minSalaryPln` (type: `string`):

No description

## `maxSalaryPln` (type: `string`):

No description

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

No description

## `publishedAt` (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 = {
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/justjoin-it-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 = { "maxItems": 200 }

# Run the Actor and wait for it to finish
run = client.actor("haketa/justjoin-it-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 '{
  "maxItems": 200
}' |
apify call haketa/justjoin-it-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,haketa/justjoin-it-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/7wTJgBBrp0i8baOZ9/builds/uAfUpZ4Wh1H4Z5hai/openapi.json
