# Free-Work Jobs Scraper - IT Missions, Day Rates (TJM) (`steadyscrape/free-work-jobs-scraper`) Actor

Scrape Free-Work (free-work.com) freelance IT missions and tech jobs in France: day rate (TJM), salary, duration, remote mode, skills, place, company and full description. Search by keyword, city, contract or minimum rate. Monitor mode. Export to JSON, CSV or Excel, or via API.

- **URL**: https://apify.com/steadyscrape/free-work-jobs-scraper.md
- **Developed by:** [Steady Scrape](https://apify.com/steadyscrape) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 jobs

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

## Free-Work Jobs Scraper: freelance IT missions, day rates (TJM) and tech jobs

**Free-Work Jobs Scraper** exports the postings of [Free-Work](https://www.free-work.com) (free-work.com), the French IT
job board: **freelance IT missions with their day rate (TJM)**, duration and remote mode, and **permanent IT jobs (CDI)
with their salary**. Search by keyword, city, department, region or country, contract, experience, minimum day rate or
salary, and get every posting with its full description, skills, place and company. Export to JSON, CSV or Excel, or
read it via API. No login, no browser.

### What the Free-Work scraper gives you

One row per posting, with:

- **Day rate (TJM)** as numbers (`dailyRateMin`, `dailyRateMax`) and as the site prints it ("500-550 €", "£430"), and the
  **yearly salary** the same way, with the currency.
- **Mission details**: contracts offered (freelance, CDI, CDD, internship, apprenticeship), duration in months, renewable,
  start date, remote mode (full, partial, on site), experience level, Free-Work's job category.
- **Skills** and soft skills the posting lists.
- **Place**: city, postal code, department, region, country, coordinates.
- **Company**: name, logo, its Free-Work profile, what the posting says about it (or about the client a consulting firm
  recruits for), and its company page when it has one.
- **Full description** as readable text and as HTML, plus the candidate profile.
- Publication, update and expiry dates (UTC), the posting's link, where to apply, and the board a posting was taken from.

### Use cases for Free-Work data

- **ESN and IT consulting firms**: watch new freelance missions and the day rates clients pay, by skill and region.
- **Recruiters and sourcing teams**: a daily feed of new missions and jobs for your clients' stacks.
- **Freelancers**: benchmark your TJM against missions with your skills in your city.
- **Market research**: day-rate and salary medians per role, skill, region and seniority.

### How to scrape Free-Work

1. Type one or more **keywords** (`Java`, `Data engineer`, `SAP FI`), or leave them empty to read every posting.
2. Pick **contract types** (freelance missions only, CDI only…) and any filters: locations, radius, remote, experience,
   minimum day rate or salary, posted within, mission duration.
3. Or paste **Free-Work search links**: make a search on free-work.com with its filters and paste the address.
4. Set **Max jobs per search** (0 = all) and run. For a daily feed, schedule it with **Only new jobs since the last run**.

#### Input example

```json
{
    "keywords": ["Java", "DevOps"],
    "contractTypes": ["contractor"],
    "locations": ["Paris", "Hauts-de-Seine"],
    "remoteModes": ["full", "partial"],
    "minDailyRate": 500,
    "postedWithin": "less_than_7_days",
    "maxJobsPerSearch": 100
}
```

Each keyword is its own search; all the locations go into each search, as on the site. Places are matched by Free-Work's
own place search ("92" is Hauts-de-Seine, "United Kingdom" is gb~~~); a place it does not know stops the searches with a
free row instead of delivering postings from everywhere.

#### Output example

```json
{
    "status": "ok",
    "jobId": 682753,
    "url": "https://www.free-work.com/fr/tech-it/job-mission/lead-developer/tech-lead-ia-15",
    "title": "Tech Lead IA",
    "contracts": ["contractor", "permanent"],
    "dailyRateMin": 500,
    "dailyRateMax": 700,
    "dailyRateText": "500-700 €",
    "salaryMin": 55000,
    "salaryMax": 75000,
    "salaryText": "55k-75k €",
    "currency": "EUR",
    "durationMonths": 6,
    "renewable": true,
    "remoteMode": "partial",
    "experienceLevel": "senior",
    "jobCategory": "Lead developer / Tech lead",
    "skills": ["AWS Cloud", "Big Data", "Cloud", "DevOps", "IA", "Large Language Model (LLM)", "MCP", "RAG"],
    "location": "Lyon, Auvergne-Rhône-Alpes",
    "city": "Lyon",
    "postalCode": "69000",
    "department": "Rhône",
    "region": "Auvergne-Rhône-Alpes",
    "countryCode": "FR",
    "companyName": "Giant Consulting",
    "description": "Dans le cadre de sa transformation digitale, un groupe international recherche un Tech Lead IA…",
    "applyUrl": "https://www.free-work.com/fr/tech-it/job-mission/lead-developer/tech-lead-ia-15",
    "publishedAt": "2026-10-15T08:50:14.000Z",
    "expiresAt": "2026-11-14T22:59:59.000Z",
    "scrapedAt": "2026-10-26T06:58:34.879Z",
    "search": { "type": "keyword", "term": "DevOps", "locations": ["Lyon, Auvergne-Rhône-Alpes"], "page": "https://www.free-work.com/fr/tech-it/jobs?query=DevOps&locations=…" }
}
```

### Free-Work output fields

| field | what it holds |
| --- | --- |
| `jobId`, `url`, `title` | Free-Work's posting id, its page, its title |
| `contracts` | `contractor` (freelance mission), `permanent` (CDI), `fixed-term` (CDD), `internship`, `apprenticeship` |
| `dailyRateMin`, `dailyRateMax`, `dailyRateText` | day rate (TJM) as numbers and as printed; null when not published |
| `salaryMin`, `salaryMax`, `salaryText` | yearly salary as numbers and as printed; null when not published |
| `currency` | `EUR` or `GBP` (null on some postings) |
| `durationMonths`, `durationValue`, `durationUnit`, `renewable`, `startsAt` | mission length (months, and as shown), renewal, start date |
| `remoteMode` | `full`, `partial` or `none` |
| `experienceLevel` | `junior`, `intermediate`, `senior` or `expert` |
| `jobCategory`, `skills`, `softSkills` | Free-Work's category and the skills listed |
| `location`, `city`, `postalCode`, `department`, `region`, `country`, `countryCode`, `latitude`, `longitude`, `locationKey` | the posting's place |
| `companyName`, `companyUrl`, `companyLogo`, `companyProfile` | the company, its Free-Work page (only companies in the site's directory have one) and profile |
| `companyDescription` | what the posting says about the company or its client |
| `description`, `descriptionHtml`, `candidateProfile` | the full posting, as text and HTML, and the profile sought |
| `applyUrl`, `sourceBoard` | where to apply, and the board a posting was taken from (`francetravail.fr`, `reed.uk`…) |
| `language`, `publishedAt`, `updatedAt`, `expiresAt` | `fr_FR` or `en_GB`, and dates in UTC |
| `search` | the search that found the posting, with the matching Free-Work search page |
| `scrapedAt` | when the row was scraped (UTC) |
| `status`, `reason` | `ok` for a posting; `no_results`, `not_found`, `invalid` or `blocked` with the reason for a search that gave none |

The run's `SUMMARY` record lists each search with the number of postings Free-Work has for it, and how each place was
matched.

### Pricing

Pay per event: **US$0.0015 per posting** (US$1.50 per 1,000), description, rates, skills and company included. Rows that
explain an empty, unknown or invalid search are free. A posting found by two searches of one run is charged once. Set a
maximum cost per run and the Actor stops at it.

### Limits and notes

- The filters are Free-Work's own: a minimum day rate or salary keeps what the site's filter keeps, and postings that do
  not publish a rate are left out by it.
- Free-Work lists about 11,000 postings, about 8,600 of them freelance missions, mostly in France and the United Kingdom,
  some in Switzerland, Belgium and Luxembourg (measured October 2026).
- Recruiters' personal contact details and Free-Work's CV database are never read.

### Is it legal to scrape Free-Work?

This Actor reads only public postings that anyone can see on free-work.com without an account, the same data the site's
own pages load, at a gentle pace. It collects no personal data about candidates and leaves out recruiters' contacts. Check
that your use of the data complies with Free-Work's terms and with the laws that apply to you (GDPR included).
**This Actor is unofficial and is not affiliated with, endorsed or sponsored by Free-Work or AGSI SAS.**

# Actor input Schema

## `keywords` (type: `array`):

Skills, roles or words as you would type them in Free-Work's search, e.g. <code>Java</code>, <code>Data engineer</code>, <code>SAP FI</code>, <code>DevOps</code>, <code>Chef de projet</code>. Each keyword is its own search with the filters below. Leave empty, with no link, to read every posting on the site (about 11,000).

## `contractTypes` (type: `array`):

Keep only postings offering one of these contracts. Freelance missions (contractor) carry a day rate (TJM); permanent jobs (CDI) a yearly salary. Empty keeps every contract.

## `locations` (type: `array`):

Cities, departments, regions or countries as people write them: <code>Paris</code>, <code>Lyon</code>, <code>Hauts-de-Seine</code> or <code>92</code>, <code>Île-de-France</code>, <code>Belgique</code>, <code>Suisse</code>, <code>United Kingdom</code>. Each is matched by Free-Work's own place search; postings in any of them are kept. A place the site does not know stops the run's searches with a free row, so nothing is charged for postings from everywhere. Leave empty for every place.

## `locationRadiusKm` (type: `integer`):

Also keep postings within this many kilometres of the locations, as the site's radius filter does. Needs a location.

## `remoteModes` (type: `array`):

Keep only postings with one of these remote modes. Empty keeps every mode, including postings that say nothing.

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

Keep only postings asking one of these levels, as the posting states it.

## `minDailyRate` (type: `integer`):

Only missions whose day rate reaches this amount, as the site's own filter applies it (in the posting's currency, mostly euros).

## `minAnnualSalary` (type: `integer`):

Only jobs whose yearly salary reaches this amount, as the site's own filter applies it.

## `postedWithin` (type: `string`):

Only postings published in the last days. Combine with a schedule and "Only new jobs" for a daily feed.

## `minDurationMonths` (type: `integer`):

Only missions lasting at least this many months.

## `maxDurationMonths` (type: `integer`):

Only missions lasting at most this many months. 48 means no maximum, as on the site.

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

Newest postings first, or the site's relevance order for the keyword.

## `searchUrls` (type: `array`):

Links of searches made on free-work.com with its filters, e.g. <code>https://www.free-work.com/fr/tech-it/jobs?query=java\&contracts=contractor\&locations=fr~ile-de-france~~</code>. Each link is its own search with the link's own filters; the form's filters above do not apply to it. Landing pages (…/jobs/java) and posting pages are not taken.

## `maxJobsPerSearch` (type: `integer`):

Stop each search after this many postings. 0 means all of them.

## `onlyNewJobs` (type: `boolean`):

Deliver, and charge, only postings that an earlier run with the same searches and filters has not delivered. Made for scheduled runs.

## Actor input object example

```json
{
  "keywords": [
    "Java"
  ],
  "contractTypes": [
    "contractor"
  ],
  "locations": [],
  "remoteModes": [],
  "experienceLevels": [],
  "postedWithin": "any",
  "sortBy": "newest",
  "searchUrls": [],
  "maxJobsPerSearch": 20,
  "onlyNewJobs": false
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `summary` (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 = {
    "keywords": [
        "Java"
    ],
    "contractTypes": [
        "contractor"
    ],
    "maxJobsPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyscrape/free-work-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 = {
    "keywords": ["Java"],
    "contractTypes": ["contractor"],
    "maxJobsPerSearch": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("steadyscrape/free-work-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 '{
  "keywords": [
    "Java"
  ],
  "contractTypes": [
    "contractor"
  ],
  "maxJobsPerSearch": 20
}' |
apify call steadyscrape/free-work-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyscrape/free-work-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/iV42EB8LlgRn1cmfc/builds/QvFsNx7wvLgGfamna/openapi.json
