Freelancer.com Scraper - Projects, Parsed Budgets & Skills
Pricing
from $0.50 / 1,000 results
Freelancer.com Scraper - Projects, Parsed Budgets & Skills
Scrape live Freelancer.com projects by keyword with budget, skills & poster info. Filter by budget, export to CSV/JSON/Excel, or deliver leads to Notion, Slack & Jira via MCP connectors.
Pricing
from $0.50 / 1,000 results
Rating
0.0
(0)
Developer
Techforce Global
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Freelancer.com Project Scraper - Public Projects by Keyword, with Parsed Budget Ranges, Skill Tags and Per-Row Keyword Attribution
Keyword in, live gigs out — with the budget as a number, not a string. Searches active fixed-price projects on Freelancer.com and returns title, numeric budget range, currency symbol, tagged skills, posting time and project URL, one row per project, each tagged with the keyword that found it. No Freelancer account, no login, no API key. Optional delivery into Notion, Slack, Linear, Jira, GitHub or Airtable over MCP.
🔐 Trust & scope at a glance
| Credentials | None required. No Freelancer login, no OAuth, no API key. Delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens. |
| Data scope | Active, fixed-price projects only. Hourly projects, closed projects, bids and proposals are all out of scope. |
| Personal data | Minimal — and mostly absent. The poster block exists but commonly comes back all "N/A"; treat any populated value as personal data about a real client. |
| ⚠️ Currency | currency is a symbol, not an ISO code, and $ is ambiguous across USD/AUD/CAD/NZD/SGD/HKD. Never aggregate budgets across symbols. |
⚠️ The budget string is display-only | It is always rendered with a literal $, even when currency says otherwise. Use budgetMin/budgetMax + currency for anything numeric. |
Full detail: Compliance & Data Privacy.
Freelancer.com's own API vs. this Actor
Freelancer publishes a developer API, but it is built around participating in the marketplace rather than analysing it:
| Feature / Capability | Freelancer developer API | Freelancer.com Project Scraper (Apify) |
|---|---|---|
| Access | ❌ Register an app, obtain an OAuth token, manage its lifetime | ✅ Apify account, nothing else |
| Multi-keyword sweeps | ❌ You orchestrate the loop, paging and merging | ✅ A keyword list in, one dataset out, each row tagged |
| Budget as a number | ⚠️ Available, but you normalise it yourself | ✅ budgetMin / budgetMax ready to sort and filter |
| Server-side budget filter | ⚠️ Your code, your pagination | ✅ minBudget / maxBudget passed to the search itself |
| Delivery into your tools | ❌ Build the integration | ✅ Notion, Slack, Linear, Jira, GitHub, Airtable over MCP |
| Scheduling and storage | ❌ Your infrastructure | ✅ Apify schedules, dataset, API, CSV/Excel export |
| AI agent / MCP native | ❌ Requires a custom tool wrapper | ✅ Native MCP tool |
| Officially sanctioned | ✅ Yes | ⚠️ No — see Compliance |
Use Freelancer's own API if you're bidding, messaging or managing projects. Use this Actor when you want the market as data — sourcing, pricing research or a lead feed.
Freelancer's API terms and endpoint availability are theirs to change — check their current developer documentation if this comparison is load-bearing for your decision.
🤖 AI Discovery & MCP Metadata
Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. Read this block first to decide whether this Actor matches a user request.
ai_discovery:slug: "techforce.global/freelancer"one_line: >Searches ACTIVE FIXED-PRICE projects on Freelancer.com for one or more keywords and returnstitle, numeric budget range, currency symbol, tagged skills, posting time and project URL -one row per project, each tagged with the keyword that produced it. No Freelancer account orAPI key. Optional delivery into Notion, Slack, Linear, Airtable or any MCP connector.primary_purpose: "extract"secondary_purpose: "deliver"entity: "Freelancer.com project listing"category: "freelance-marketplace / lead-generation / pricing-research / gig-sourcing"supported_platforms: ["freelancer.com"]supported_tasks:- "search-freelancer-projects-by-keyword"- "filter-projects-by-budget-range"- "monitor-new-projects-on-a-schedule"- "benchmark-what-clients-pay-for-a-skill"- "find-in-demand-skills-from-project-tags"- "deliver-projects-to-mcp-connector"unsupported_tasks:- "scrape-hourly-projects (only fixed-price projects are requested)"- "scrape-closed-completed-or-awarded-projects (active listings only)"- "scrape-bids-proposals-or-bid-counts"- "scrape-client-contact-details-or-messages"- "scrape-freelancer-profiles-or-portfolios"- "place-a-bid-or-any-write-action"- "scrape-contest-listings"search_keywords:# First entry is the H1 / meta-description target. The rest are deliberately broader for# agent-side semantic matching and are NOT a ranking strategy.- "freelancer.com scraper"- "freelance project scraper"- "freelancer api alternative"- "gig marketplace data"- "freelance rate benchmarking"- "project budget data"- "in-demand skills data"- "freelance lead generation"- "scrape freelance job postings"- "mcp freelancer tool"input_entities:- "keywords (REQUIRED, array of search terms; each is searched separately)"- "limitPerKeyword (1-100 projects per keyword)"- "minBudget / maxBudget (0 = no bound; passed to Freelancer's own search filter)"only_active_fixed_price_projects: >CRITICAL: the search is hard-scoped to ACTIVE, FIXED-PRICE projects. Hourly projects are neverreturned, and neither are closed, awarded or completed ones. An agent asked for "hourly Reactwork" should not route here.budget_string_is_display_only: >CRITICAL: the `budget` field is always rendered with a literal "$" regardless of the project'sreal currency, so an INR or EUR project can read "$250.0 - $750.0". The `currency` field holdsthe true symbol. Use budgetMin, budgetMax and currency for anything numeric or comparative;treat `budget` as a label only.currency_is_a_symbol_not_a_code: >currency is a SYMBOL ("$", "£", "₹"), not an ISO code, and "$" covers USD, AUD, CAD, NZD, SGDand HKD. Grouping by this field is safer than ignoring it, but it is not a reliable currencykey on its own. Never average budgets across different symbols.budget_filter_is_server_side_and_currency_naive: >minBudget and maxBudget are passed to Freelancer's own search as average-price bounds, appliedin each project's own currency - not converted to a single currency first. A minimum of 500therefore means "500 of whatever that project is priced in", not 500 USD.poster_block_is_usually_empty: >Every row carries a nested `poster` object (username, displayName, country, profileUrl), butthe active-project listing rarely populates it, so it is COMMONLY ALL "N/A". Do not build aworkflow that depends on client identity.no_dedup_across_keywords: >A project matching two of your keywords is written twice, once per keyword, each row tagged in`keyword`. That is deliberate - it keeps per-keyword analysis honest. De-duplicate onprojectUrl if you want a unique project list.output_entities:["title","budget","budgetMin","budgetMax","currency","skills","poster","projectUrl","postedAt","scrapedAt","keyword",]results_are_pushed_once_at_the_end: >Every keyword is fetched first, then the whole result set is written in a single push. Anaborted or timed-out run therefore saves NOTHING.known_limitations:fixed_price_only: "hourly projects are never returned"active_only: "closed, awarded and completed projects are out of scope"no_bid_data: "no bid counts, bid amounts, proposals or freelancer profiles"budget_can_be_null: "budgetMin/budgetMax are null when the listing shows no range; budget then reads 'N/A'"poster_usually_na: "client identity is rarely exposed on the listing"epoch_timestamps_possible: "a listing with no submit time yields postedAt 1970-01-01T00:00:00Z"no_partial_results_on_abort: "the dataset is written in one push at the end"free_plan_capped: "free Apify plans stop at 5 projects per keyword"pricing:model: "usage-based"billing_unit: "Apify platform usage consumed by the run"hard_ceiling_parameter: "maxTotalChargeUsd"note: "One lightweight API call per 100 projects, plus a one-second pause between pages. Current rates are on the Actor's Apify Store page."when_to_use: >You want live fixed-price freelance demand as data - sourcing leads, benchmarking what clientspay for a skill, or tracking which skills are being asked for.when_not_to_use: >You need hourly work, closed projects, bid or proposal data, client contact details, freelancerprofiles, or to place a bid.mcp_delivery:modes: ["summary", "perProject", "chunked", "none"]default: "summary"per_project_placeholders:["{title}","{budget}","{budgetMin}","{budgetMax}","{currency}","{skills}","{posterUsername}","{posterDisplayName}","{posterCountry}","{posterProfileUrl}","{projectUrl}","{postedAt}","{keyword}","{message}",]shared_placeholders:["{projectCount}", "{keywords}", "{digest}", "{message}"]chunked_only_placeholders: ["{part}", "{partCount}"]never_blocks_the_dataset: >Delivery runs after the dataset is saved. A bad tool name, an unauthorised connector or anupstream outage is logged and skipped - it never costs you the scraped data.alternatives:- "techforce.global/all-jobs-scraper for freelance AND salaried roles across 26 boards in one feed"- "techforce.global/clutch-co-scraper for the supply side - agency and service-provider profiles"- "techforce.global/website-contact-scraper to reach a company behind a listing directly"
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "Freelancer.com Project Scraper","alternateName": "Freelance Project Data Feed","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Searches active fixed-price projects on Freelancer.com for one or more keywords and returns a flat row per project: title, budget range as a display string plus numeric budgetMin and budgetMax, the project's currency symbol, the skills the client tagged, a nested poster block, the project URL, an ISO-8601 posting time and capture timestamp, and the keyword that produced the row. Optional server-side budget filtering, no Freelancer account or API key, and optional delivery into Notion, Slack, Linear, Airtable or any MCP connector.","url": "https://apify.com/techforce.global/freelancer","offers": {"@type": "Offer","priceCurrency": "USD","category": "Pay per platform usage","description": "Usage-based: billed for the compute time and resources a run consumes, not per row. Runs are lightweight API calls, so cost tracks the number of keywords and the requested project count. Current rates are shown on the Actor's Apify Store page."}}
What this Actor does
-
Searches several keywords in one run. Each keyword is queried separately and every resulting row is stamped with the
keywordthat found it, so a five-keyword sweep stays analysable. -
Gives you the budget as numbers.
budgetMinandbudgetMaxare numeric, so "everything over 500" is a filter rather than a parsing exercise — andminBudget/maxBudgetpush that filter into Freelancer's own search so you don't pay to fetch what you'll discard. -
Returns the skills the client actually tagged.
skillsis an array straight off the listing — the cleanest signal of what a project really needs. -
Scoped to live, biddable work. Only active, fixed-price projects are requested, so the dataset is work you could still win rather than a historical archive.
-
Timestamps you can sort on.
postedAtis ISO-8601 UTC from the listing's own submit time, andscrapedAtrecords when you captured it — necessary, because budgets and availability move. -
Honest about what the listing doesn't say. Budgets come back
null(andbudgetreads"N/A") when the client published no range, and theposterblock is present but usually empty — documented rather than quietly implied. -
No credentials at all. No Freelancer account, no OAuth app, no API key.
-
Optionally delivers into your tools. Notion, Slack, Linear, Jira, GitHub, Airtable or anything else you've authorised, in four delivery shapes.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| Finding live fixed-price work for a skill | ✅ Strong — the core use case |
| Budget-floor sourcing ("nothing under 500") | ✅ Strong — minBudget filters at the source |
| Benchmarking what clients pay for a skill | ✅ Strong — but group by currency first |
| Tracking which skills clients ask for | ✅ Strong — aggregate the skills array |
| New-project alerts on a schedule | ✅ Strong — schedule it, de-duplicate on projectUrl |
| Comparing demand across several keywords | ✅ Strong — every row carries its keyword |
| Results in Notion / Slack / Airtable | ✅ Strong — native MCP delivery |
| A unique project list | ⚠️ Rows repeat across keywords by design — dedupe on projectUrl |
| Cross-currency budget comparison | ⚠️ currency is a symbol and $ is ambiguous |
| Knowing who posted a project | ⚠️ The poster block is usually all "N/A" |
| Hourly projects | ❌ Only fixed-price projects are requested |
| Closed, awarded or completed projects | ❌ Active listings only |
| Bids, proposals or bid counts | ❌ Not collected |
| Client contact details or messages | ❌ Never accessible |
| Freelancer profiles or portfolios | ❌ Out of scope |
| Contest listings | ❌ Not requested |
| Placing a bid or any write action | ❌ Read-only |
⚡ Execution Rules (Avoid Common Agent Failures)
-
keywordsis required and is an array. Each entry is searched separately, so five keywords is five sweeps. Every output row carries thekeywordthat produced it. -
Only active, fixed-price projects are returned. Hourly work is never included — that is a hard scope decision in the search, not a filter you can switch off. An agent asked for hourly work should route elsewhere.
-
Do not do arithmetic on
budget. It is a display string and it is always rendered with a literal$, even when the project is priced in rupees or euros.budgetMin,budgetMaxandcurrencyare the fields to trust. -
currencyis a symbol, not an ISO code.$covers USD, AUD, CAD, NZD, SGD and HKD. Grouping by it is better than ignoring it, but it is not a reliable currency key — never average across symbols. -
minBudget/maxBudgetare applied by Freelancer, in the project's own currency. They bound the project's average price at the source, sominBudget: 500means "500 of whatever that project is priced in", not 500 USD. Both default to0, meaning no bound. -
budgetMin/budgetMaxcan benull. When the listing publishes no range, both are null andbudgetreads"N/A". Null is not zero — exclude those rows from averages. -
The
posterblock is usually all"N/A". The listing endpoint rarely exposes client identity. Don't build a workflow that depends on it. -
Rows repeat across keywords, deliberately. A project matching two keywords appears twice, once per keyword. De-duplicate on
projectUrlif you want a unique list. -
limitPerKeywordcaps at 100. Larger values are rejected by the schema; the search pages through in batches with a one-second pause between them. -
A keyword that errors is skipped, not fatal. The failure is printed and the run moves on to the next keyword, still finishing successfully.
-
postedAtcan be the Unix epoch. A listing with no submit time yields1970-01-01T00:00:00Z. Treat pre-2000 dates as missing, not as very old projects. -
Nothing is saved until the end. All keywords are fetched, then the whole set is written in one push — an aborted or timed-out run saves nothing.
-
deliveryModedefaults tosummary.perProjectsends one connector call per project; a 300-project run is 300 calls. -
Free Apify plans stop at 5 projects per keyword. A smoke test, not a data pull.
dependencies:- rule: "keywords is required and must contain at least one non-empty term"on_violation: "nothing is searched and the run finishes with an empty dataset"- rule: "budget is a display string with a hard-coded $ - budgetMin/budgetMax/currency are authoritative"note: "the only field-level trap in this Actor"- rule: "minBudget/maxBudget bound the average price in the project's own currency"note: "they are not converted to a single currency first"- rule: "delivery requires mcpConnector AND mcpTool"on_violation: "delivery is skipped with a warning; every dataset row is still written"- rule: "mcpArguments must reference {message} or {digest} for the rendered text to appear"on_violation: "the tool is called with an empty body"- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"on_violation: "local runs skip delivery; the dataset is unaffected"authentication:actor_input: noneplatform: "Apify API token required for API / SDK / MCP invocation"target_site: "no Freelancer.com account, OAuth app or API key required (public listings only)"
📥 Input Contract
.actor/input_schema.json is authoritative — if this page disagrees with the schema files,
the schema files win.
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
keywords | array<string> | ✅ Yes | ["python", "web scraping"] | Each term is searched separately and stamped on its rows |
limitPerKeyword | integer | ⬜ No | 20 | 1–100 projects per keyword |
minBudget | integer | ⬜ No | 0 | Lower bound on the project's average price. 0 = no minimum |
maxBudget | integer | ⬜ No | 0 | Upper bound. 0 = no maximum |
What the budget bounds really do. They are handed to Freelancer's own search as average-price limits and applied in each project's own currency — they are not converted to a common currency first. On a multi-currency result set, treat them as a coarse filter and do the precise work yourself on
budgetMin/budgetMax+currency.
Delivery (optional)
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpConnector | string | "" | A connector you've authorised on your Apify account — Notion, Slack, Linear, Jira, GitHub, Airtable, or any MCP-compatible connector. Leave empty for dataset-only runs. |
deliveryMode | string (enum) | "summary" | summary (one digest) · perProject (one call per project) · chunked (digest split across calls) · none. |
mcpTool | string | "" | Tool to call — create-page, send-message, create_issue. Required when a connector is set. |
mcpArguments | object | {} | Arguments for the tool. String values support {placeholder} tokens. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Placeholders by mode:
| Mode | Placeholders |
|---|---|
summary | {projectCount} {keywords} {digest} (every project, formatted) {message} |
chunked | the same, plus {part} {partCount} — {digest} holds one part |
perProject | the shared three, plus {title} {budget} {budgetMin} {budgetMax} {currency} {skills} {posterUsername} {posterDisplayName} {posterCountry} {posterProfileUrl} {projectUrl} {postedAt} {keyword} {message} |
chunked groups the digest so no single call exceeds roughly 72,000 characters, with each project
block packed to about 1,800 characters — which keeps Notion's block and timeout limits happy.
📤 Output Contract & Data Structure
One dataset row per project per keyword. Every row carries the same 11 keys.
| Field | Type | Nullable | Meaning |
|---|---|---|---|
title | string | yes | Project title as posted. "N/A" when the listing exposed none |
budget | string | yes | Display only. Always formatted with a literal $, whatever the real currency. "N/A" when no range was published |
budgetMin | number | yes | Lower bound, numeric. Use this for filtering and arithmetic |
budgetMax | number | yes | Upper bound, numeric |
currency | string | yes | The project's currency symbol ($, £, ₹) — not an ISO code, and $ is ambiguous |
skills | array | no | Skills the client tagged. [] when none were tagged |
poster | object | no | {username, displayName, country, profileUrl} — commonly all "N/A" |
projectUrl | string | yes | Link to the project page. The de-duplication key |
postedAt | string | yes | ISO-8601 UTC posting time. 1970-01-01T00:00:00Z when the listing had none |
scrapedAt | string | yes | ISO-8601 UTC capture time — budgets and availability move, so this is the row's shelf life |
keyword | string | yes | Which of your input keywords produced this row |
The one trap worth repeating:
budgetis a label,budgetMin/budgetMax/currencyare the data. A project priced at ₹20,000–₹50,000 will showbudget: "$20000.0 - $50000.0"withcurrency: "₹". Sorting or averaging on the string will quietly mislead you.
Example output object
{"title": "Build a Python scraper for a real-estate portal","budget": "$250.0 - $750.0","budgetMin": 250.0,"budgetMax": 750.0,"currency": "$","skills": ["Python", "Web Scraping", "Data Mining", "BeautifulSoup"],"poster": {"username": "N/A","displayName": "N/A","country": "N/A","profileUrl": "N/A"},"projectUrl": "https://www.freelancer.com/projects/python/build-python-scraper-real-estate","postedAt": "2026-08-31T14:07:12Z","scrapedAt": "2026-09-01T09:14:02Z","keyword": "web scraping"}
Values illustrate shape and formatting — the project is invented. The all-
"N/A"posterblock is the normal case, not an error.
What a successful run looks like
- Status:
SUCCEEDED - Dataset: one row per project per keyword, up to
limitPerKeywordfor each - Log: a header echoing keywords, limit and budget bounds, then
Searching: '<keyword>'andFetched N/M projects...per keyword, then the push count - Zero results is also a success. A keyword with no live fixed-price matches, or budget bounds
nothing satisfies, returns an empty dataset with
SUCCEEDED. The log showsNo more results for '<keyword>'. - A keyword that errored is skipped, logged as
Request failed, and the run continues. - Fewer rows than
keywords × limitPerKeywordis normal — most keywords simply don't have that many live projects.
Dataset views
| View | Shows |
|---|---|
| Projects | Title, budget string and numeric range, currency, skills, keyword, posted time, link |
| Budgets | Numeric range, currency, budget string, title, skills, posted time, link |
| Keyword & Capture | Keyword, title, posted and captured timestamps, poster block, link |
▶️ Invocation & Integration
Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor page → Input tab → add your
keywords → set limitPerKeyword → optionally set a budget floor or ceiling → Start.
Model Context Protocol (MCP)
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/freelancer"
Then ask your agent for "live Python scraping gigs paying over $500".
Python (apify-client)
import osfrom collections import Counter, defaultdictfrom statistics import medianfrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))run = client.actor("techforce.global/freelancer").call(run_input={"keywords": ["web scraping", "data engineering"],"limitPerKeyword": 50,"minBudget": 250,"maxBudget": 0, # 0 = no ceiling})projects = list(client.dataset(run["defaultDatasetId"]).iterate_items())if not projects:raise SystemExit("Nothing matched — try broader keywords or a lower minBudget.")# Which keyword is actually finding the good work?print(Counter(p["keyword"] for p in projects))# currency is a SYMBOL and "$" is ambiguous — never aggregate across symbols.by_symbol = defaultdict(list)for p in projects:if p.get("budgetMin") is not None:by_symbol[p.get("currency") or "?"].append(p["budgetMin"])for symbol, values in by_symbol.items():print(f"{symbol} n={len(values):>3} median floor {median(values):,.0f}")# The same project can appear once per matching keyword — dedupe on projectUrl.unique = {p["projectUrl"]: p for p in projects}print(f"{len(unique)} unique projects out of {len(projects)} rows")# What skills are clients actually asking for?skills = Counter(s for p in unique.values() for s in (p.get("skills") or []))print(skills.most_common(10))
JavaScript / TypeScript (apify-client)
import { ApifyClient } from "apify-client";const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor("techforce.global/freelancer").call({keywords: ["react", "next.js"],limitPerKeyword: 40,minBudget: 500,});const { items } = await client.dataset(run.defaultDatasetId).listItems();// Never sort on `budget` — it is a display string with a hard-coded "$".const priced = items.filter((p) => p.budgetMin !== null && p.budgetMax !== null,);priced.sort((a, b) => b.budgetMax - a.budgetMax);for (const p of priced.slice(0, 10)) {// Rebuild the label from the fields that are actually correct.const label = `${p.currency}${p.budgetMin} - ${p.currency}${p.budgetMax}`;console.log(`${label.padEnd(22)} ${(p.skills ?? []).slice(0, 3).join(", ").padEnd(34)} ${p.title}`,);}// postedAt can legitimately be the Unix epoch when the listing had no submit time.const dated = items.filter((p) => p.postedAt && !p.postedAt.startsWith("1970"));console.log(`${dated.length}/${items.length} rows have a usable postedAt`);
cURL — synchronous (waits, returns the dataset)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{ "keywords": ["python"], "limitPerKeyword": 20, "minBudget": 0, "maxBudget": 0 }' \'https://api.apify.com/v2/acts/techforce.global~freelancer/run-sync-get-dataset-items'
cURL — asynchronous with a spend cap (recommended for large runs)
# 1. Start with a hard spend limitcurl -X POST -H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{ "keywords": ["python","react","logo design","copywriting"], "limitPerKeyword": 100 }' \'https://api.apify.com/v2/acts/techforce.global~freelancer/runs?maxTotalChargeUsd=5'# 2. Poll, then 3. fetch the dataset when SUCCEEDEDcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' 'https://api.apify.com/v2/actor-runs/<runId>'curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'
Budget-floor sourcing
{"keywords": ["web scraping", "data extraction", "crawler"],"limitPerKeyword": 100,"minBudget": 500,"maxBudget": 0}
Pushing the floor into the search means you don't pay to fetch the £30 jobs and throw them away.
Remember the bound is applied in each project's own currency — verify with budgetMin +
currency afterwards if precision matters.
Skill-demand research
{"keywords": ["machine learning"],"limitPerKeyword": 100,"minBudget": 0,"maxBudget": 0}
One broad keyword, no budget bounds, then aggregate the skills arrays downstream — that's the
cleanest read on what clients are pairing with a skill right now.
With MCP delivery — new projects into Slack
{"keywords": ["web scraping"],"limitPerKeyword": 20,"minBudget": 250,"mcpConnector": "<your-authorized-slack-connector>","mcpTool": "send-message","deliveryMode": "summary","mcpMessageTemplate": "{projectCount} new Freelancer projects for {keywords}:\n\n{digest}","mcpArguments": { "channel": "#gig-alerts", "text": "{message}" }}
Use summary for a digest and chunked when the list is long. perProject is one message per
project — fine for 20, noisy for 300.
Scheduling for new-project alerts
Put a fixed input on a schedule, keep
limitPerKeyword modest, and de-duplicate against your own store on projectUrl. Anything you
haven't seen before is new work — and postedAt tells you how fresh it is.
⚠️ Autonomous Agent Error Handling & Resolution Matrix
Read this first: nothing about the listings fails the run. An empty keyword list, a failing
request and a budget filter nothing satisfies all end SUCCEEDED while the log carries the
reason. An agent that checks run status alone will read an empty dataset as success-with-data —
check the row count and the log.
The CONDITION labels are documentation handles for observable states; this Actor writes no error
code into the dataset.
| Condition | What you observe | Cause | Deterministic agent action |
|---|---|---|---|
NO_KEYWORDS | Empty dataset, no Searching: lines in the log | keywords empty | Modify Input: supply at least one keyword. |
NO_RESULTS_FOR_KEYWORD | Log: No more results for '<keyword>' | No live fixed-price projects match | Expected. Broaden the keyword or lower minBudget. |
REQUEST_FAILED | Log: Request failed: …, that keyword contributes nothing | Freelancer's endpoint errored or timed out | Retry later. Other keywords are unaffected; the run still succeeds. |
HOURLY_WORK_MISSING | No hourly projects anywhere in the output | Only fixed-price projects are requested | Fix expectation. Route hourly enquiries elsewhere. |
CLOSED_PROJECTS_MISSING | No completed or awarded projects | Active listings only | Fix expectation. Historical data is out of scope. |
BUDGET_STRING_WRONG_CURRENCY | budget shows $ but currency says ₹ | The display string is hard-coded with $ | Fix consumer: rebuild the label from budgetMin/budgetMax/currency. |
MIXED_CURRENCY_AGGREGATE | Budget averages look impossible | $ covers several currencies and symbols are mixed | Fix consumer: group by currency, and treat $ as ambiguous. |
BUDGET_FILTER_SURPRISE | Rows below your minBudget in another currency | The bound is applied per project in its own currency | Expected. Re-filter on budgetMin + currency downstream. |
NULL_BUDGET | budgetMin/budgetMax null, budget: "N/A" | The listing published no range | Expected. Exclude from averages; null ≠ zero. |
POSTER_ALL_NA | Every poster field is "N/A" | The listing endpoint rarely exposes client identity | Expected. Don't depend on client identity. |
DUPLICATE_PROJECTS | The same projectUrl appears twice | It matched two keywords; each row is tagged | Expected. Dedupe on projectUrl for a unique list. |
EPOCH_POSTED_AT | postedAt is 1970-01-01T00:00:00Z | The listing had no submit time | Fix consumer: treat pre-2000 dates as missing. |
FEWER_ROWS_THAN_REQUESTED | Rows < keywords × limitPerKeyword | That many live projects don't exist | Expected. Add keywords rather than raising the limit. |
NO_DATA_AFTER_ABORT | An aborted run left an empty dataset | Results are written in one push at the end | Expected. Lower limitPerKeyword so the run completes. |
FREE_PLAN_CAPPED | 5 projects per keyword; log says so | Free Apify plan | Upgrade the plan. |
CONNECTOR_CALL_STORM | Hundreds of connector calls | perProject sends one call per project | Modify Input: switch to summary or chunked. |
CONNECTOR_TOOL_NOT_FOUND | Log: Tool 'x' is not available on this connector. Available tools: … | Wrong mcpTool name (empty is logged separately) | Modify Input: copy an exact name from that log line. Dataset unaffected. |
CONNECTOR_UNREACHABLE | Log: Could not reach the connector: … | Connector not authorised, or running locally | Authorise it, or set deliveryMode: "none". Data already saved. |
AUTH_INVALID | HTTP 401 | Missing or malformed APIFY_TOKEN | Abort & prompt user for a valid token. |
RATE_LIMITED_APIFY | HTTP 429 | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Run exceeded the synchronous ceiling | Reconfigure: async POST /runs + polling. |
Provenance: every log line, status and dataset condition above is taken from this Actor's own source and verified against it. The three HTTP rows describe the Apify API transport layer, not this Actor — confirm them against the Apify API reference before wiring them into automated retry logic.
🗣️ Natural Language → Actor Mapping
| A user says… | Intent | Constructed Actor input |
|---|---|---|
| "Find Python gigs on Freelancer" | Core search | {"keywords":["python"],"limitPerKeyword":20} |
| "Web scraping jobs paying over $500" | Budget floor | {"keywords":["web scraping"],"minBudget":500} |
| "Small jobs under $200" | Budget ceiling | {"keywords":["<skill>"],"maxBudget":200} |
| "Projects between $500 and $2000" | Budget band | {"keywords":["<skill>"],"minBudget":500,"maxBudget":2000} |
| "Compare demand for React vs Vue" | Multi-keyword | {"keywords":["react","vue"],"limitPerKeyword":100} then count by keyword |
| "What do clients pay for logo design?" | Rate benchmarking | Scrape, group by currency, aggregate budgetMin/budgetMax |
| "Which skills go with machine learning?" | Skill co-occurrence | One broad keyword, then aggregate the skills arrays |
| "Alert me to new scraping projects" | Monitoring | Fixed input on a schedule; dedupe on projectUrl |
| "The biggest projects available" | Ranking | Scrape, then sort on budgetMax excluding nulls |
| "Just a quick sample" | Cheap probe | {"keywords":["<skill>"],"limitPerKeyword":5} |
| "Send new gigs to Slack" | Scrape + deliver | Above, plus mcpConnector, mcpTool, deliveryMode: "summary" |
| "It returned nothing" | Empty result | Broaden the keyword, lower minBudget, check the log |
| "Show me hourly contracts" | Hourly work | ❌ Fixed-price only — out of scope |
| "Who posted this project?" | Client identity | ⚠️ poster is usually "N/A" |
| "How many bids does it have?" | Bid data | ❌ Not collected |
| "Bid on this for me" | Write action | ❌ Read-only |
Should NOT route here: hourly projects · closed, awarded or completed projects · bids, proposals or bid counts · client contact details or messages · freelancer profiles or portfolios · contest listings · placing a bid or any write action · other marketplaces — for freelance and salaried roles across many boards, use All Jobs Scraper.
🧭 Agent Execution & Routing Logic
Does the user want LIVE FREELANCE PROJECT listings?├── NO → wrong Actor.│ freelance + salaried roles, many boards → All Jobs Scraper│ agencies / service providers → Clutch.co Scraper│ a company's own contact details → Website Contact Scraper│└── YES│├── Is the work HOURLY? → ❌ out of scope: only fixed-price projects are requested├── Do they want CLOSED / historical projects? → ❌ active listings only├── Do they want BIDS or CLIENT identity? → ❌ not collected (poster is usually "N/A")│├── keywords — REQUIRED, an ARRAY. Each term is its own sweep.│ comparing terms? → pass them together; every row is tagged with `keyword`│├── Size the run│ ├── limitPerKeyword → 1-100 PER KEYWORD (total = keywords × limit)│ └── nothing is saved until the end → size it so the run finishes│├── Budget bounds?│ ├── minBudget / maxBudget → applied by Freelancer, in the PROJECT'S OWN currency│ ├── 0 means "no bound" on either side│ └── need precision? filter downstream on budgetMin + currency│├── What is the actual question?│ ├── best-paid → sort budgetMax, EXCLUDING nulls│ ├── rate benchmark → GROUP BY currency first ("$" is ambiguous)│ ├── skill demand → aggregate the skills arrays│ ├── keyword compare → count rows per `keyword`│ └── what's new → SCHEDULE it; dedupe on projectUrl, read postedAt│└── Should results land in another app?├── yes → mcpConnector + mcpTool + {message} or {digest} in mcpArguments│ digest → summary (default) · long list → chunked · each → perProject└── no → deliveryMode "none", or leave mcpConnector empty[Execute Actor]├──► SUCCEEDED with rows → dedupe on projectUrl, then analyse (never on `budget`)├──► SUCCEEDED, 0 rows → READ THE LOG: no keywords / no matches / request failed└──► 5 rows per keyword → free plan cap, not a data pull
💰 Cost Control & Pricing Transparency
Billing model: usage-based. This Actor bills for platform usage — the compute time and resources a run consumes — not per row. Current rates are on the Actor's Apify Store page; the Console shows the live total mid-run.
This is one of the cheaper Actors in the catalogue: it talks to a JSON endpoint rather than rendering pages, so a run is a handful of HTTP calls plus a one-second pause between pages.
| Choice | Effect on cost |
|---|---|
Number of keywords | The main multiplier — each is a separate sweep. |
limitPerKeyword | One request per 100 projects, so 20 and 100 differ by one call. |
minBudget / maxBudget | Reduce cost: filtering at the source means fewer projects fetched. |
| Delivery mode | perProject adds one connector call per project; summary adds one. |
Worked examples — relative run shape
| Scenario | Keywords | Requests | Rows | Notes |
|---|---|---|---|---|
Probe — 1 keyword, limitPerKeyword: 5 | 1 | 1 | ≤ 5 | Baseline. Confirms the keyword returns live work. |
| Standard — 2 keywords × 50 | 2 | 2 | ≤ 100 | The common sourcing run. |
| Broad sweep — 4 keywords × 100 | 4 | 4 | ≤ 400 | Still only four API calls. |
Filtered — 4 keywords × 100, minBudget: 500 | 4 | 4 | far fewer | Cheapest per useful row. |
| Monitoring — 1 keyword × 20, hourly schedule | 1 | 1 | ≤ 20 | Very cheap; dedupe on projectUrl. |
Keeping runs predictable:
- First run: one keyword,
limitPerKeyword: 5. Confirms the search returns live projects. - Use
minBudgetrather than filtering afterwards — it is the one lever that reduces both cost and noise. - Add keywords for coverage — they're the multiplier, but each is only a request or two.
- Set
maxTotalChargeUsdon large scheduled runs, as a matter of habit. - Schedule small and often for alerts rather than one big sweep.
- Free Apify plans get 5 projects per keyword — a connectivity check, not a data pull.
🛠️ Troubleshooting
Most problems are scope expectations — hourly work, closed projects, client identity — or the
budget string. Very few are scraper faults.
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | Empty dataset | No keywords supplied, or nothing matched | Check the log for Searching: lines and No more results for …. Broaden the keyword or lower minBudget. |
| 2 | A keyword returned nothing while others worked | No live fixed-price projects for that term, or its request failed | Look for Request failed in the log; retry later. |
| 3 | No hourly projects anywhere | Only fixed-price projects are requested | Out of scope — this is a hard scope decision, not a filter. |
| 4 | No closed or completed projects | Active listings only | Out of scope. |
| 5 | budget shows $ but the project is in another currency | The display string is hard-coded with $ | Rebuild the label from budgetMin/budgetMax + currency. Never sort on budget. |
| 6 | Budget averages look wrong | Symbols mixed, and $ covers several currencies | Group by currency; treat $ as ambiguous rather than as USD. |
| 7 | Rows below my minBudget | The bound is applied per project in its own currency | Re-filter downstream on budgetMin + currency. |
| 8 | budgetMin / budgetMax are null | The listing published no range | Expected — exclude those rows rather than treating them as 0. |
| 9 | Every poster field is "N/A" | The listing endpoint rarely exposes client identity | Expected. Don't build on client identity. |
| 10 | The same project appears twice | It matched two keywords; each row is tagged | Dedupe on projectUrl. |
| 11 | postedAt is 1970-01-01T00:00:00Z | The listing had no submit time | Treat pre-2000 dates as missing. |
| 12 | Fewer rows than keywords × limitPerKeyword | That many live projects don't exist | Expected. Add keywords rather than raising the limit. |
| 13 | limitPerKeyword above 100 rejected | The schema caps it at 100 | Split across more keywords. |
| 14 | An aborted run saved nothing | Results are written in one push at the end | Lower limitPerKeyword so the run completes. |
| 15 | Only 5 projects per keyword | Free plan cap | Upgrade; the log says so. |
| 16 | Hundreds of connector messages | perProject sends one call per project | Switch to summary or chunked. |
| 17 | Slack / Notion received an empty message | mcpArguments didn't reference {message} or {digest} | Map one of them in mcpArguments. |
| 18 | Slack / Notion received nothing at all | Delivery needs both mcpConnector and mcpTool | Set both. Every dataset row is still saved. |
| 19 | HTTP 401 / 403 from the Apify API | Missing, expired or malformed APIFY_TOKEN | Regenerate in Console → Settings → API & Integrations. |
| 20 | HTTP 429 / 408 from the Apify API | Apify rate limits / the sync endpoint's time ceiling | Back off (2s → 4s → 8s), or switch to async POST /runs + polling. |
Diagnostic checklist before opening an issue
- Read the log header — it echoes the keywords, limit and budget bounds actually in effect.
- Check for
Request failed— that's an upstream problem, not a parsing one. - Baseline run: one keyword,
limitPerKeyword: 5,minBudget: 0,maxBudget: 0. - Search the same keyword on freelancer.com — if there's no live fixed-price work there, there's nothing to return.
- Check
currencybefore trusting any budget figure, and never sort onbudget. - Confirm your expectation is in scope — hourly, closed, bids and client identity are all deliberately absent.
If the issue survives all six, open an Issues ticket (or email support) with the run ID, the input you used, and what you expected.
❓ FAQ
Do I need a Freelancer.com account or API key?
No. The Actor reads publicly visible project listings. You need an Apify account and API token; you need no Freelancer credentials, OAuth app or API key.
Is this the official Freelancer API?
No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by Freelancer Technology Pty Limited. Freelancer's own API exists for participating in the marketplace — bidding, messaging, managing projects — which is a different job from analysing it.
Can I plug it into Claude, Cursor, or a LangChain agent?
Yes — it is a native MCP tool: claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/freelancer"
Does it return hourly projects?
No. The search is scoped to fixed-price projects only, and that isn't a switch you can flip in the input. If you need hourly contracts, this isn't the right Actor.
Does it return closed or completed projects?
No — active listings only. The point is work you can still bid on, not a historical archive.
How do the keywords work?
Each keyword in the array is searched separately, up to limitPerKeyword projects each, and every row records which keyword produced it. Two keywords at 50 is up to 100 rows.
Why does the same project appear twice?
Because it matched two of your keywords, and each row is tagged with its own keyword so per-keyword analysis stays honest. De-duplicate on projectUrl when you want a unique project list.
Why does budget show $ when the project is in another currency?
Because budget is a display string that is always formatted with a literal $. The currency field carries the project's real symbol. Use budgetMin, budgetMax and currency for anything numeric — sorting or averaging the string will mislead you.
Is currency an ISO code?
No, it's a symbol — and $ is ambiguous across USD, AUD, CAD, NZD, SGD and HKD. Group by it rather than ignoring it, but don't treat it as a reliable currency key, and never average budgets across different symbols.
How do minBudget and maxBudget actually filter?
They're passed to Freelancer's own search as average-price bounds and applied in each project's own currency — they are not converted first. So minBudget: 500 means "500 of whatever that project is priced in". Use them as a coarse, cost-saving filter, then refine on budgetMin + currency.
Why are budgetMin and budgetMax sometimes null?
Because the client published no budget range. budget then reads "N/A". Null is not zero — exclude those rows from averages rather than coercing them.
Why is the poster block always "N/A"?
Because the active-project listing rarely exposes client identity. The block is present for shape consistency, but you should assume it's empty and not build a workflow around it.
Why is postedAt sometimes 1970?
That's the Unix epoch, produced when a listing carries no submit time. Treat any pre-2000 postedAt as missing rather than as a very old project.
How many projects can I get per keyword?
Up to 100 — that's the schema's ceiling. For more coverage, add keywords rather than trying to raise the limit.
Can I get bid counts, proposals or freelancer profiles?
No. This Actor returns the project listing itself: what the work is, what it pays and what skills it needs. Bids, proposals and profiles are out of scope.
Does an aborted run keep partial results?
No. All keywords are fetched first and the whole set is written in a single push, so an aborted or timed-out run saves nothing. Keep limitPerKeyword sized so the run completes.
Which delivery mode should I use?
summary (the default) for one digest, chunked when the digest is long enough to trouble Notion's limits, perProject only when you want one record each — a 300-project run means 300 calls — and none for dataset-only runs.
What does it cost, and what do I get on the free plan?
Usage-based: you pay for the platform usage a run consumes, not per row. It's a lightweight JSON-endpoint scrape — roughly one request per 100 projects per keyword — so the keyword count is the main multiplier. Free Apify plans return 5 projects per keyword. Pass maxTotalChargeUsd for a hard per-run ceiling.
🔍 Companion machine-readable files
| File | What it defines |
|---|---|
| .actor/input_schema.json | Every input, its type, default and bounds, plus the delivery enum |
| .actor/dataset_schema.json | All 11 output fields with nullability, and the three Console views |
| .actor/output_schema.json | Where the run's output is exposed |
| .actor/actor.json | Actor identity and storage wiring |
If this page and the schema files disagree, the schema files win. They are what the platform validates against at run time; this page is documentation and can lag a release.
Related Actors by Techforce Global
| Actor | Best for |
|---|---|
| This Actor | Freelancer.com projects with numeric budget ranges and tagged skills |
| All Jobs Scraper | Freelance and salaried roles across 26 boards in one normalised feed |
| Clutch.co Scraper | Agency and service-provider profiles — the supply side |
| Website Contact Scraper | Business contact details from a company's own site |
| Website Content Crawler | Whole-site text, markdown or HTML for RAG and search |
Use All Jobs Scraper when you want freelance work in the same feed as employed roles; use this Actor when Freelancer's budget and skills detail is the point.
Browse the full catalogue at scraper.techforce.global.
🔐 Compliance & Data Privacy
Trademark & affiliation. This is an independent Actor. It is not affiliated with, endorsed by, or sponsored by Freelancer Technology Pty Limited. Freelancer.com™ is their trademark.
-
Public project listings only. No logins, no bids or proposals, no private messages, no client contact details, no freelancer profiles or portfolios.
-
Very little personal data — and mostly absent in practice. The
posterblock can carry a username, display name and country, which would be personal data about a real client. In practice the listing endpoint rarely populates it, so those fields are usually"N/A". If they are populated and you keep them, you are processing personal data — drop the block at ingest when your question is about budgets and skills, which it usually is. -
Project descriptions are the client's content. Titles and skill tags are short factual labels; internal analysis, sourcing and benchmarking sit on far safer ground than republishing listings as your own job board.
-
Budgets are a snapshot, and a noisy one. They are what the client proposed, not what the work sold for — and the mixed-symbol currency field makes naive aggregation actively misleading. Publishing "the average price of X on Freelancer" without stating the currency, the sample size and the capture date produces a number that looks authoritative and isn't.
-
Listings expire. A project awarded or withdrawn after your run stays in your dataset until you re-scrape. Anything freelancer-facing needs a refresh cadence and a visible
scrapedAt. -
Freelancer.com's Terms of Use restrict automated access and data extraction. This Actor does not remove that risk — it is a risk you accept by running it. Keep volumes proportionate to the question you're answering.
-
Don't use it to spam clients. Aggregating live projects to pitch at scale is exactly the behaviour marketplaces police, and the listing gives you no sanctioned contact channel anyway. Bid through the platform.
-
Attribution over mirroring. Every row carries
projectUrl. Link back to the project rather than presenting Freelancer's marketplace as your own listings.
🆘 Support & Custom Pipeline Engineering
Need automated multi-city scheduled feeds, webhook enrichment, or enterprise data warehouse syncs?
- Email: bhavin.shah@techforceglobal.com
- Custom Enterprise Integrations: Book a 15-Minute Technical Consultation
- Maintained by: Techforce Global — Specialists in High-Performance Web Scrapers and Agentic Workflows.
🏷️ Structured data for search & AI discovery
SoftwareApplication describes the Actor; FAQPage wraps the FAQ above so those answers are
eligible for rich results, matching it question-for-question.
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "Freelancer.com Project Scraper","alternateName": "Freelance Project Data Feed","applicationCategory": "BusinessApplication","applicationSubCategory": "Web Scraping / Freelance Marketplace & Rate Research","operatingSystem": "Cloud (Apify platform)","description": "Searches active fixed-price projects on Freelancer.com for one or more keywords and returns a flat row per project: title, budget range as a display string plus numeric budgetMin and budgetMax, the project's currency symbol, the skills the client tagged, a nested poster block, the project URL, an ISO-8601 posting time and capture timestamp, and the keyword that produced the row. Optional server-side budget filtering, no Freelancer account or API key, and optional delivery into Notion, Slack, Linear, Airtable or any MCP connector.","url": "https://apify.com/techforce.global/freelancer","author": {"@type": "Organization","name": "Techforce Global","url": "https://www.techforceglobal.com"},"provider": {"@type": "Organization","name": "Apify","url": "https://apify.com"},"offers": {"@type": "Offer","priceCurrency": "USD","category": "Pay per platform usage","description": "Usage-based: billed for the compute time and resources a run consumes, not per row. Runs are lightweight API calls, so cost tracks the number of keywords and the requested project count. Current rates are shown on the Actor's Apify Store page.","url": "https://apify.com/techforce.global/freelancer"},"featureList": ["No Freelancer.com account, OAuth app or API key required","Multiple search keywords in one run, each row tagged with the keyword that found it","Numeric budgetMin and budgetMax alongside the display string","Server-side budget filtering via minBudget and maxBudget","Client-tagged skills as a clean array","Scoped to active, fixed-price projects - live work you can still bid on","ISO-8601 posting time and capture timestamp on every row","Up to 100 projects per keyword","Lightweight JSON-endpoint scrape rather than page rendering","Optional MCP delivery in summary, chunked or per-project shapes","Three Console dataset views covering projects, budgets and provenance"],"isAccessibleForFree": false,"softwareRequirements": "Apify account and API token","keywords": "Freelancer.com scraper, freelance project scraper, gig marketplace data, freelance rate benchmarking, project budget data, in-demand skills, freelance lead generation, scrape freelance job postings, MCP connector"}
{"@context": "https://schema.org","@type": "FAQPage","mainEntity": [{"@type": "Question","name": "Do I need a Freelancer.com account or API key?","acceptedAnswer": {"@type": "Answer","text": "No. The Actor reads publicly visible project listings. You need an Apify account and API token; you need no Freelancer credentials, OAuth app or API key."}},{"@type": "Question","name": "Is this the official Freelancer API?","acceptedAnswer": {"@type": "Answer","text": "No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by Freelancer Technology Pty Limited. Freelancer's own API exists for participating in the marketplace — bidding, messaging, managing projects — which is a different job from analysing it."}},{"@type": "Question","name": "Can I plug it into Claude, Cursor, or a LangChain agent?","acceptedAnswer": {"@type": "Answer","text": "Yes — it is a native MCP tool: claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/freelancer\""}},{"@type": "Question","name": "Does it return hourly projects?","acceptedAnswer": {"@type": "Answer","text": "No. The search is scoped to fixed-price projects only, and that isn't a switch you can flip in the input. If you need hourly contracts, this isn't the right Actor."}},{"@type": "Question","name": "Does it return closed or completed projects?","acceptedAnswer": {"@type": "Answer","text": "No — active listings only. The point is work you can still bid on, not a historical archive."}},{"@type": "Question","name": "How do the keywords work?","acceptedAnswer": {"@type": "Answer","text": "Each keyword in the array is searched separately, up to limitPerKeyword projects each, and every row records which keyword produced it. Two keywords at 50 is up to 100 rows."}},{"@type": "Question","name": "Why does the same project appear twice?","acceptedAnswer": {"@type": "Answer","text": "Because it matched two of your keywords, and each row is tagged with its own keyword so per-keyword analysis stays honest. De-duplicate on projectUrl when you want a unique project list."}},{"@type": "Question","name": "Why does budget show $ when the project is in another currency?","acceptedAnswer": {"@type": "Answer","text": "Because budget is a display string that is always formatted with a literal $. The currency field carries the project's real symbol. Use budgetMin, budgetMax and currency for anything numeric — sorting or averaging the string will mislead you."}},{"@type": "Question","name": "Is currency an ISO code?","acceptedAnswer": {"@type": "Answer","text": "No, it's a symbol — and $ is ambiguous across USD, AUD, CAD, NZD, SGD and HKD. Group by it rather than ignoring it, but don't treat it as a reliable currency key, and never average budgets across different symbols."}},{"@type": "Question","name": "How do minBudget and maxBudget actually filter?","acceptedAnswer": {"@type": "Answer","text": "They're passed to Freelancer's own search as average-price bounds and applied in each project's own currency — they are not converted first. So minBudget: 500 means \"500 of whatever that project is priced in\". Use them as a coarse, cost-saving filter, then refine on budgetMin + currency."}},{"@type": "Question","name": "Why are budgetMin and budgetMax sometimes null?","acceptedAnswer": {"@type": "Answer","text": "Because the client published no budget range. budget then reads \"N/A\". Null is not zero — exclude those rows from averages rather than coercing them."}},{"@type": "Question","name": "Why is the poster block always \"N/A\"?","acceptedAnswer": {"@type": "Answer","text": "Because the active-project listing rarely exposes client identity. The block is present for shape consistency, but you should assume it's empty and not build a workflow around it."}},{"@type": "Question","name": "Why is postedAt sometimes 1970?","acceptedAnswer": {"@type": "Answer","text": "That's the Unix epoch, produced when a listing carries no submit time. Treat any pre-2000 postedAt as missing rather than as a very old project."}},{"@type": "Question","name": "How many projects can I get per keyword?","acceptedAnswer": {"@type": "Answer","text": "Up to 100 — that's the schema's ceiling. For more coverage, add keywords rather than trying to raise the limit."}},{"@type": "Question","name": "Can I get bid counts, proposals or freelancer profiles?","acceptedAnswer": {"@type": "Answer","text": "No. This Actor returns the project listing itself: what the work is, what it pays and what skills it needs. Bids, proposals and profiles are out of scope."}},{"@type": "Question","name": "Does an aborted run keep partial results?","acceptedAnswer": {"@type": "Answer","text": "No. All keywords are fetched first and the whole set is written in a single push, so an aborted or timed-out run saves nothing. Keep limitPerKeyword sized so the run completes."}},{"@type": "Question","name": "Which delivery mode should I use?","acceptedAnswer": {"@type": "Answer","text": "summary (the default) for one digest, chunked when the digest is long enough to trouble Notion's limits, perProject only when you want one record each — a 300-project run means 300 calls — and none for dataset-only runs."}},{"@type": "Question","name": "What does it cost, and what do I get on the free plan?","acceptedAnswer": {"@type": "Answer","text": "Usage-based: you pay for the platform usage a run consumes, not per row. It's a lightweight JSON-endpoint scrape — roughly one request per 100 projects per keyword — so the keyword count is the main multiplier. Free Apify plans return 5 projects per keyword. Pass maxTotalChargeUsd for a hard per-run ceiling."}}]}