# Freelancer.com Projects Scraper (`scrapyx/freelancer-projects-scraper`) Actor

Active freelance projects from Freelancer.com: title, full description, budget range, bid count and average bid, required skills, project type and posting time. Filter by keyword, skill or budget, with skill names resolved to the numeric ids the site needs.

- **URL**: https://apify.com/scrapyx/freelancer-projects-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Jobs
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

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

## Freelancer.com Projects Scraper

Active freelance projects from **Freelancer.com** — title, full description,
budget range, bid count and average bid, required skills, project type and
posting time.

### Why use this actor

- **Type skill names, not ID numbers.** Freelancer's own filter only accepts
  numeric skill ids, and there are 3,483 of them. Type `PHP`, `Game Design` or
  `web-scraping` and this actor looks the id up for you — and if the skill does
  not exist, it says so instead of returning an empty result that looks like
  "no work available".
- **Bid competition on every row.** How many freelancers have already bid, and
  what the average bid is — the two numbers that decide whether a project is
  worth your time.
- **The full brief**, not the truncated preview the listing shows.
- **Paging that actually stops.** Past the end of the results the API quietly
  serves the same last page over and over, forever. This actor detects that and
  stops; a naive loop would re-export the same 99 projects indefinitely.
- **The whole skill taxonomy** — 3,483 skills with ids, slugs and categories —
  available as its own output in one extra request.
- **No account, no API key, no login.**

### How it works

1. You describe the work you want: keywords, skills, project type, budget.
2. Skill names are resolved against Freelancer's live list before anything is
   searched.
3. The actor pages through active projects, 99 per request, deduplicating as it
   goes, and stops when the results run out.
4. Results land in your dataset as JSON, CSV or Excel, with a summary row
   describing the run.

### Input

```json
{
  "query": "python",
  "skills": ["PHP"],
  "projectTypes": [],
  "includeProjectDetails": true,
  "includeSkillTaxonomy": false,
  "maxItems": 300,
  "maxPages": 5,
  "minRequestInterval": 0.5,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

| Field | Type | Description |
|---|---|---|
| `query` | string | Free-text search. A real filter — `python` narrowed 5,723 active projects to 172. |
| `skills` | array | Skill name (`PHP`), slug (`php`) or numeric id (`3`) — all three resolve. An unknown one is reported as an error. |
| `includeSkillTaxonomy` | boolean | Also emit all 3,483 skills with ids, slugs and categories. One extra request. Default `false`. |
| `projectTypes` | array | `fixed`, `hourly`, or empty for both. |
| `minAveragePrice` / `maxAveragePrice` | integer | Bounds on the average bid for fixed-price work. |
| `minAverageHourly` / `maxAverageHourly` | integer | Bounds on the average hourly bid. |
| `includeProjectDetails` | boolean | Full description, skills, attachments and location rather than the short preview. Same number of requests either way. Default `true`. |
| `maxItems` | integer | Stop after this many unique projects. `0` = no cap. Default `300`. |
| `maxPages` | integer | Request ceiling. 99 projects per page, ~5,700 active in total, so ~58 pages covers everything. Default `5`. |
| `minRequestInterval` | number | Seconds between requests. Default `0.5`. |
| `proxyConfiguration` | object | Apify Proxy. Datacenter is the default and is sufficient. |

> **Currency is not normalised.** Projects are posted in USD, CAD, AUD, INR and
> others. Each row carries its own `currencyCode`, and the budget filters
> compare raw numbers.

### Output

Four record types share one dataset, told apart by `recordType`.

#### `PROJECT`

```json
{
  "_input": "query='python'",
  "_source": "S1-projectsapi",
  "_scrapedAt": "2026-09-16T04:34:20Z",
  "recordType": "PROJECT",
  "id": 40714249,
  "title": "Python Script for Amazon Shift-Picking",
  "projectUrl": "https://www.freelancer.com/projects/selenium/Python-Script-for-Amazon-Shift",
  "type": "fixed",
  "status": "active",
  "frontend_project_status": "open",
  "budgetMinimum": 250.0,
  "budgetMaximum": 750.0,
  "currencyCode": "CAD",
  "bidCount": 67,
  "bidAverage": 404.33,
  "bidperiod": 7,
  "submittedAt": "2026-09-16T04:26:26Z",
  "updatedAt": "2026-09-16T04:26:26Z",
  "skillNames": ["JavaScript", "Python", "Linux", "Web Scraping", "Selenium", "… 4 more"],
  "skillIds": [9, 13, 31, 95, 116, 696, 1679, 1977, 2165],
  "preview_description": "I rely on Amazon's internal shift-picking portal and I'm tired of hammering the refresh ke…",
  "description": "(full brief)",
  "featured": false,
  "urgent": false,
  "local": false,
  "language": "en",
  "employerAvailable": false,
  "pageNumber": 1
}
```

All 84 of Freelancer's own fields ship alongside these.

| Field | Description |
|---|---|
| `id` / `projectUrl` | Project id and a direct link. |
| `title` / `description` | Title and the full brief. |
| `preview_description` | The first ~100 characters of the brief, cut mid-word — a truncation, not a summary. |
| `type` | `fixed` or `hourly`. |
| `budgetMinimum` / `budgetMaximum` / `currencyCode` | The advertised budget, in the poster's own currency. |
| `bidCount` / `bidAverage` | How crowded the project already is, and at what price. |
| `bidperiod` | How many days bidding stays open. |
| `submittedAt` / `updatedAt` | ISO 8601 UTC, alongside the raw unix fields. |
| `skillNames` / `skillIds` | Required skills, flattened from the nested objects. |
| `featured` / `urgent` / `local` / `nonpublic` | Freelancer's own listing flags. |
| `employerAvailable` | Always `false` — see Known limits. |

#### `SEARCH_SUMMARY`

| Field | Description |
|---|---|
| `upstreamTotal` | How many active projects match your filters. |
| `projectsReturned` / `pagesFetched` / `duplicatesDropped` | What the run did. |
| `skillsFiltered` | The skills your names resolved to, with their ids. |
| `stoppedBecause` | `max_items_reached`, `max_pages_reached`, `no_new_projects`, `no_more_results`. |
| `offsetClampsPastEnd` | Always `true` — why paging stops on "no new ids" rather than on an empty page. |
| `employerNotAvailableAnonymously` | Always `true`. |

#### `SKILL`

```json
{"recordType": "SKILL", "skillId": 3, "skillName": "PHP", "skillSlug": "php",
 "categoryId": 1, "categoryName": "Websites, IT & Software"}
```

Paste `skillName`, `skillSlug` or `skillId` into `skills`.

#### `ERROR`

| `_error` | Meaning |
|---|---|
| `invalid_input` | Refused before any request — an unknown skill (with suggestions), a bad project type, or an inverted budget range. |
| `api_rejected` | Freelancer refused the request itself. |
| `unexpected_shape` | A success response without the structure this actor depends on. |
| `fetch_failed` | Network or server trouble after retries. |

### Known limits

- **The employer is not public.** Freelancer's API accepts a `user_details`
  flag and always returns an empty users object with a null owner id to
  anonymous callers, so who posted a project is not available. Every row is
  flagged rather than carrying a field that is permanently empty.
- **Bids are counted, not listed.** You get how many and the average; the
  individual bids are behind the same boundary.
- **Active projects only.** There is no public archive of completed work.
- **99 projects per request** is the site's cap, and asking for more returns 99
  without saying so.
- **Currency is not converted.**

# Actor input Schema

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

Free-text search across active projects, e.g. 'python', 'logo design', 'shopify'. A real upstream filter — 'python' narrowed 5,723 active projects to 172 during testing.

## `skills` (type: `array`):

Filter by required skill. Type the skill NAME ('PHP', 'Game Design'), its URL slug ('php') or its numeric id — all three resolve. Freelancer's own API only accepts the numeric id and has 3,483 of them, so this actor looks them up for you. A skill it does not recognise is reported as an error, because the API answers an unknown id with zero projects and a success status — which looks exactly like 'no work available'.

## `includeSkillTaxonomy` (type: `boolean`):

Emit all 3,483 skills with their ids, categories and slugs as their own rows. This is where the skill names above come from. Costs one extra request.

## `projectTypes` (type: `array`):

Restrict to 'fixed' price projects, 'hourly' ones, or leave empty for both.

## `minAveragePrice` (type: `integer`):

Keep only fixed-price projects whose average bid is at least this much, in the project's own currency. Note the currency is not normalised — projects are posted in USD, AUD, INR and others, and each row carries its own.

## `maxAveragePrice` (type: `integer`):

Upper bound on the average bid, same currency caveat as the minimum.

## `minAverageHourly` (type: `integer`):

For hourly projects: the lowest average hourly bid to keep, in the project's own currency.

## `maxAverageHourly` (type: `integer`):

For hourly projects: the highest average hourly bid to keep.

## `includeProjectDetails` (type: `boolean`):

Ask Freelancer for the full description, skills, attachments, location and upgrade flags rather than the short preview. Same number of requests either way — it is a flag on the same call — so there is rarely a reason to turn it off.

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

Stop after this many unique projects. 0 means no cap (still bounded by max pages).

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

Safety ceiling on requests. Freelancer serves at most 99 projects per call, and there were about 5,700 active projects in total during testing — so roughly 58 pages covers everything on offer.

## `minRequestInterval` (type: `number`):

Shortest gap between two request starts. Pages are fetched one after another, so this is the only speed control that matters.

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

Apify Proxy settings. Datacenter is the default and is sufficient — the API answered normally to a plain unproxied request during testing, with no key and no cookie.

## Actor input object example

```json
{
  "query": "python",
  "includeSkillTaxonomy": false,
  "includeProjectDetails": true,
  "maxItems": 300,
  "maxPages": 5,
  "minRequestInterval": 0.5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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": "python"
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/freelancer-projects-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": "python" }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/freelancer-projects-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": "python"
}' |
apify call scrapyx/freelancer-projects-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/freelancer-projects-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/mSWZUlpFh6W8AlCBh/builds/mg8X2axQPWNEwmord/openapi.json
