# Workana Jobs Scraper (`dami_studio/workana-jobs-scraper`) Actor

Scrape open Workana freelance projects across the English, Spanish and Portuguese boards. Every row carries the title, full brief, category and skills, budget band, fixed or hourly, proposal count, client country and rating, and how long ago it was posted. No account needed.

- **URL**: https://apify.com/dami\_studio/workana-jobs-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 project returneds

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

## Workana Jobs Scraper: projects, budgets, proposals and client data

Workana is where a lot of Latin American freelance work gets posted, and the board is public: you
can read every open project without an account. This actor reads it and hands you clean rows:
title, the client's full brief, category and skill tags, the budget band, how many people have
already pitched, which country the client is in, and how long ago it went up.

Export as JSON, CSV or Excel. Nothing to log into, no API key, no browser.

### Read this before you buy

**Workana serves a logged-out visitor about seven projects per page, and its own result counter
counts more than that.** A search that says "896 results found" hands an anonymous caller roughly
317 of them. Measured across nine different searches on 19 September 2026, the share landed between
35% and 42% every time, and it is deterministic: asking for the same page five times returns the
same seven projects, not seven new ones.

So: every row you get is real, complete and correctly parsed. But if you need *literally every*
project matching a filter, this will not give you that, and no keyless tool can. The way to widen
coverage is to run several narrower searches rather than one broad one. See "Getting more of the
board" below.

That is the single most important thing to know, so it is at the top rather than in a footnote.

### Three boards, not one

Workana keeps English, Spanish and Portuguese projects in **separate catalogues**, and a project
sits on exactly one. They do not overlap at all: the same four pages returned 30 English projects
and 32 Spanish ones with not a single project in common.

The English board is small, under a thousand open projects. Spanish and Portuguese are each an
order of magnitude bigger. If you are looking for volume, tick Spanish and Portuguese. If you only
read English, you are seeing a genuinely narrow slice of Workana, and that is Workana's doing, not
this actor's.

The `projectLanguage` field on every row says which board it came from.

### What you get, per project

| Field | Example |
|---|---|
| `projectId`, `title`, `projectUrl` | `comprehensive-brand-identity-redesign-for-perspective-therapy-center` |
| `description` | the client's full brief, tags stripped, paragraphs kept |
| `category`, `subcategory` | `Design & Multimedia`, `Corporate image` |
| `projectDetails` | the extra answers Workana collects, e.g. `{"What is the scope of the project?": "Define from scratch"}` |
| `skills`, `skillSlugs` | `["Graphic Design","Logo design","Adobe Illustrator"]` |
| `projectType` | `fixed` or `hourly` |
| `budgetText`, `budgetMin`, `budgetMax`, `budgetCurrency`, `budgetUnit` | `USD 500 - 1,000`, `500`, `1000`, `USD`, `project` |
| `proposalsCount` | `44` |
| `clientCountry`, `clientCountryCode` | `United States`, `US` |
| `clientNameMasked`, `clientRating`, `clientHasRating`, `clientPaymentVerified`, `clientPlan` | `C. M.`, `0`, `false`, `true`, `null` |
| `postedRelative`, `postedAt`, `postedAtPrecision` | `Yesterday`, `2026-09-19T00:20:28Z`, `day` |
| `isFeatured`, `isUrgent` | booleans |
| `projectLanguage`, `searchUrl`, `page`, `position`, `scrapedAt` | `en`, the exact search this came from |

#### Budgets are bands, and the band means different things

Workana does not ask clients for a number. It asks them to pick a bracket, and there are ten:

```
fixed price   Less than USD 50 · 50-100 · 100-250 · 250-500 · 500-1,000 · 1,000-3,000 · Over USD 3,000
hourly        Less than USD 15/hour · USD 15-45/hour · Over USD 45/hour
```

`budgetMin` and `budgetMax` are the edges of that bracket, and **an open-ended bracket leaves the
open side null**: "Less than USD 50" gives you `budgetMin: null, budgetMax: 50`. That is why
`budgetMin` is populated on only about 57% of rows; it is not a gap in the parsing, it is the shape
of the data. `budgetUnit` is `project` or `hour`, and on an hourly project `USD 15 - 45` means per
hour, not for the job. Every row says which, so you never have to guess.

`budgetText` is the untouched string if you would rather do your own thing with it.

#### Dates are an age, not a timestamp

The board publishes "5 hours ago" or "2 weeks ago", never a date. `postedRelative` is exactly what
Workana said. `postedAt` is that converted to a real instant, and `postedAtPrecision` tells you how
much to trust it: `exact`, `hour`, `day`, `week` or `month`. A row marked `week` could be six days
either side. Most of the board is fresh (in a 168-project sample, "yesterday" and "two to five days
ago" were the commonest values), so this matters less than it sounds, but it is worth knowing
before you sort by it.

### Input

```json
{
  "searchQuery": "wordpress",
  "languages": ["es", "pt"],
  "categories": ["it-programming"],
  "clientCountries": ["BR", "AR"],
  "maxProposals": 5,
  "maxItems": 200
}
```

Everything is optional. A keyword on its own works; so does a category on its own; so does pasting
a Workana search URL you already built in the browser into `startUrls`.

- **`searchQuery`**: free text, in whatever language the board you picked uses.
- **`languages`**: which boards to read. Each is searched separately.
- **`categories` / `subcategories` / `skills` / `clientCountries`**: Workana's own filters, applied
  on their side. Use the slug from the site's own URL: `it-programming`, `web-development`,
  `adobe-photoshop`, `BR`. A skill slug that does not exist matches nothing rather than erroring,
  so check one against a live search first.
- **`projectType`, `maxProposals`, `minProposals`, `postedWithinDays`**: applied here, after
  Workana answers, because the site has no server-side filter for any of them. Projects dropped by
  these are never charged.
- **`maxItems`**: the hard cap on rows.

Each combination of language, category and country is run as its own search, so two languages and
three countries is six searches. That is deliberate and it is how you get past the per-search
ceiling. **`maxItems` is shared out across those searches** rather than being spent on the first
one: tick three boards and you get roughly a third from each, not 150 English rows and nothing
else. A search with less to give hands its unused share to the ones after it.

#### Getting more of the board

One search stops at 99 pages, and a logged-out caller gets about seven projects per page. The way
around that is to slice, not to page harder:

- Tick all three languages. That alone roughly triples what you see.
- Add several categories instead of searching the whole board. `it-programming` and
  `design-multimedia` are separate searches with separate ceilings.
- Add client countries. `BR`, `MX`, `AR`, `CO` and `ES` are the big ones.

Duplicates are removed across every search in a run, so overlapping slices cost you nothing.

### A real row

```json
{
  "projectId": "comprehensive-brand-identity-redesign-for-perspective-therapy-center",
  "title": "Comprehensive Brand Identity Redesign for Perspective Therapy Center",
  "projectUrl": "https://www.workana.com/job/comprehensive-brand-identity-redesign-for-perspective-therapy-center",
  "description": "We are seeking a talented graphic designer to undertake a comprehensive brand identity redesign...",
  "category": "Design & Multimedia",
  "subcategory": "Corporate image",
  "skills": ["Graphic Design", "Logo design", "Corporate Brand Identity", "Adobe Illustrator"],
  "projectType": "fixed",
  "budgetText": "USD 500 - 1,000",
  "budgetMin": 500,
  "budgetMax": 1000,
  "budgetCurrency": "USD",
  "budgetUnit": "project",
  "proposalsCount": 44,
  "clientCountry": "United States",
  "clientCountryCode": "US",
  "clientNameMasked": "C. M.",
  "clientRating": 0,
  "clientPaymentVerified": true,
  "postedRelative": "Yesterday",
  "postedAt": "2026-09-19T00:20:28.735Z",
  "postedAtPrecision": "day",
  "projectLanguage": "en"
}
```

### Measured field coverage

77 projects parsed across all three boards, 19 September 2026:

| Field | Present |
|---|---|
| `title`, `description`, `projectUrl` | 100% |
| `category`, `subcategory` | 100% |
| `skills` | 100% |
| `budgetText`, `budgetCurrency`, `budgetUnit`, `projectType` | 100% |
| `budgetMax` | 97.4% (open-ended top brackets have none) |
| `budgetMin` | 57.1% (open-ended bottom brackets have none) |
| `proposalsCount` | 100% |
| `clientCountry`, `clientCountryCode` | 100% |
| `clientRating`, `clientPaymentVerified` | 100% |
| `postedRelative`, `postedAt` | 100% |
| `clientPlan` | 2.6%; only a handful of clients carry a plan badge |

Across a separate 24-search run covering keywords, all eight categories, subcategories, countries
and skills, every single search answered: 24 of 24.

### What this does not do

- **It does not return every project Workana counts.** Roughly 35-42%, as measured above. The rest
  is behind a sign-in, and this actor does not sign in to anything.
- **It does not give you the client's name.** Workana masks it to initials for anonymous visitors.
  `clientNameMasked` is `C. M.`, and that is genuinely all there is. It does not give you their
  email, their company, their spend history or their other projects either.
- **It does not give you exact budget figures**, because Workana never collects them, only the
  bracket the client picked.
- **It does not give you a publication timestamp**, only an age, converted and labelled with its
  precision.
- **It does not read proposals, messages or freelancer profiles.** Open projects only.
- **It does not go past page 99** of a single search. Workana answers 404 there.
- **It does not filter by budget.** Workana's budget controls are a range slider that did not
  behave as a server-side filter in testing, and shipping a filter that silently does nothing is
  worse than not shipping one. Every row carries `budgetMin`/`budgetMax`, so filter after export.

### Billing

You are charged **per project returned**: one charge per row in your dataset, and nothing else.

Free, always:

- The sample row you get back from an empty input.
- Diagnostic rows: a bad URL, a search that returned nothing, a search that failed.
- Projects dropped by `projectType`, `maxProposals`, `minProposals` or `postedWithinDays`. They are
  filtered before anything is pushed, so they never reach your dataset and never bill.
- Duplicates. A project that appears in two of your searches is charged once.

A run that finds nothing costs you the run fee and nothing more.

### FAQ

**Do I need a Workana account or an API key?**
No. Neither. The actor reads the same public project board a logged-out visitor sees.

**Why do I get fewer projects than the search says it found?**
Because Workana shows a logged-out visitor about seven projects per page while counting all of
them. It is their gate, not a limit in this actor. Tick more languages and add categories to widen
the slice.

**Why is the English board so small?**
Workana is a Latin American marketplace. Most of its clients post in Spanish or Portuguese. Under a
thousand open English projects against ten thousand-plus on each of the other two boards is normal.

**Can I get the client's name or contact details?**
No. Workana shows anonymous visitors initials only, and this actor does not sign in. If you need
the full name you need a Workana account of your own.

**Are the budgets real numbers?**
They are real brackets. Workana asks clients to pick a range, not type an amount, so `USD 500 -
1,000` is the client's actual answer. There is no more precise figure to be had.

**Is `postedAt` accurate?**
To the hour on anything posted in the last day, to the day within a week, coarser after that.
`postedAtPrecision` says which on every row, and `postedRelative` preserves exactly what Workana
printed.

**Can I paste a search I built on the site?**
Yes. Copy the address bar into `startUrls` and the keyword, category, skills, country and language
are read straight out of it.

**How do I find fresh work nobody has bid on?**
Set `maxProposals` to 5 and `postedWithinDays` to 2. Plenty of rows come back with
`proposalsCount: 0`.

**What happens if Workana blocks a request?**
You get an uncharged diagnostic row saying so, and the run still finishes as succeeded. You are
never charged for a search that failed.

# Actor input Schema

## `searchQuery` (type: `string`):

Free-text search across open Workana projects, e.g. "wordpress", "diseño de logo", "tradução". Leave it empty to take whatever is newest on the boards you selected below.

## `languages` (type: `array`):

Workana keeps three separate boards and a project sits on exactly one of them. They do not overlap: the same four pages returned 30 English projects and 32 Spanish ones with nothing in common. Spanish and Portuguese are far larger than English, so tick those if you want volume.

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

Hard cap on projects returned across the whole run. You are charged per project you receive; sample and diagnostic rows are free. One search reaches at most 99 pages, so a very large number only pays off if you also tick more languages or add categories.

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

Workana's top-level categories. Use the slug from the site's own URL ("it-programming", "design-multimedia", "sales-marketing", "writing-translation", "admin-support", "legal", "finance-management", "engineering-manufacturing"). Each category is searched separately.

## `subcategories` (type: `array`):

More specific than a category, and again the slug from the URL: "web-development", "mobile-development", "e-commerce", "wordpress-1", "data-science-1". Searched separately from the categories above, not combined with them.

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

Only return projects tagged with these skills. Use the slug Workana uses in its own skill links — "adobe-photoshop", "microsoft-excel", "logo-design-1". A wrong slug quietly matches nothing rather than erroring, so check one against a live Workana search first.

## `clientCountries` (type: `array`):

Two-letter country codes for the client who posted the project: "BR", "MX", "AR", "CO", "US", "ES". Each is searched separately. Brazil dominates the Portuguese board; Argentina, Colombia and Mexico the Spanish one.

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

Paste the address bar from a Workana search you already set up, e.g. https://www.workana.com/jobs?category=it-programming\&language=es — the filters are read straight out of it. When you use this, the fields above are ignored except the keyword.

## `projectType` (type: `string`):

Fixed-price projects quote a total; hourly projects quote a rate per hour. Workana has no server-side filter for this, so it is applied here after the listing is read — projects that do not match are dropped and never charged.

## `maxProposals` (type: `integer`):

Keep only projects with at most this many proposals. Set it to 5 if you are looking for work nobody has quoted on yet — plenty of rows come back at 0.

## `minProposals` (type: `integer`):

Keep only projects with at least this many proposals. Useful the other way round, for reading what a busy brief looks like.

## `postedWithinDays` (type: `integer`):

Keep only projects posted in the last N days. Workana publishes an age ("5 hours ago", "2 weeks ago") rather than a timestamp, so this is accurate to the hour on fresh projects and to the week on older ones. Every row carries postedAtPrecision so you can see which.

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

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "searchQuery": "wordpress",
  "languages": [
    "en"
  ],
  "maxItems": 50,
  "categories": [],
  "subcategories": [],
  "skills": [],
  "clientCountries": [],
  "startUrls": [],
  "projectType": "any",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per Workana project: title, full description, category and subcategory, skill tags, budget band with currency, fixed or hourly, proposal count, the client's country, star rating and payment-verified flag, how long ago it was posted, and the project URL. An empty input writes a single uncharged sample row instead; blocked or empty searches write uncharged diagnostic rows.

# 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 = {
    "searchQuery": "wordpress",
    "languages": [
        "en"
    ],
    "maxItems": 50,
    "categories": [],
    "subcategories": [],
    "skills": [],
    "clientCountries": [],
    "startUrls": [],
    "projectType": "any",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/workana-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 = {
    "searchQuery": "wordpress",
    "languages": ["en"],
    "maxItems": 50,
    "categories": [],
    "subcategories": [],
    "skills": [],
    "clientCountries": [],
    "startUrls": [],
    "projectType": "any",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/workana-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 '{
  "searchQuery": "wordpress",
  "languages": [
    "en"
  ],
  "maxItems": 50,
  "categories": [],
  "subcategories": [],
  "skills": [],
  "clientCountries": [],
  "startUrls": [],
  "projectType": "any",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/workana-jobs-scraper --silent --output-dataset

```

## MCP server setup

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