moxlade/guides/export upwork freelancers to csv

How to export Upwork freelancers to CSV or JSON — a filtered list with rates, JSS, earnings and contracts, in one run

Upwork has no export button for talent search, and its API needs a reviewed key. This is the route that works today: run our Upwork freelancer scraper on Apify with the filters you want, then download the result as CSV, JSON, Excel or XML — 65 typed columns per freelancer, from a census of 30,042 profiles. About five minutes the first time.

published 2026-09-13 · updated 2026-09-13

The steps

  1. Open the actor. moxlade/upwork-freelancers on the Apify Store. A free Apify account is enough; the actor bills per delivered row on top of it.
  2. Set what you want. A query (matched against title, overview and skills), then the filters: rateMin/rateMax, jssMin, topRated, countries, hoursMin, lastWorkedWithinDays, skills, and so on. Set maxItems — it is both the row cap and the spending cap.
  3. Start the run. A stored search of 500 rows answers in one call, in seconds. A liveSearch asks Upwork now and takes under a minute for 25 rows.
  4. Export. On the run's Output tab, choose Export → CSV, JSON, Excel, XML, HTML or RSS. Pick the columns you want, or take all 65.

Or pull it over the API

One request runs the actor and returns the rows in the format you name — for a pipeline, a sheet that refreshes, or an agent:

curl -X POST "https://api.apify.com/v2/acts/moxlade~upwork-freelancers/run-sync-get-dataset-items?token=YOUR_TOKEN&format=csv" \
  -H 'Content-Type: application/json' \
  -d '{"query": "python developer", "topRated": true, "countries": ["United States"], "rateMin": 50, "rateMax": 100, "maxItems": 200}'

format=json, xlsx or xml work the same way; add &fields=name,title,rate,jss,total_earnings,url to keep only the columns you need. The Apify client libraries for Python and JavaScript wrap the same call — ready-to-run scripts for every export on this page are in getmoxlade/upwork-freelancers-examples.

What the columns are

groupcolumns
identityciphertext (the stable id to join on), person_id, url, name, title, overview
placecountry, country_code, state, city, region, timezone
moneyrate (USD/h), total_earnings, earnings_precision (exact / band), earnings_hidden
track recordjss (percentage), jss_hidden, hours, jobs_total, jobs_hourly, jobs_fixed, contracts_total, hire_again_pct, rating, feedback_count, recent_hours, recent_jobs, member_since, last_worked_on
badges and flagsbadge, top_rated, top_rated_plus, availability, contract_to_hire, id_verified, phone_verified, agency_name
skillsskills (display names, an array), portfolio_count
provenancesource (stored / live), fetched_at, age_days, first_seen_at, last_seen_at, versions, changes, status (public / private), private_since
on request (include)contracts (every completed contract with client feedback and the freelancer's feedback about the client), contracts_in_progress, education, employment_history, languages, certificates, other_experiences, portfolios, testimonials, specialized_profiles
Arrays (skills, contracts, …) export to CSV as JSON strings in one cell; take JSON or Excel when you want them expanded.

Three exports people actually run

A shortlist for a role

query + topRated + countries + a rate band + jssMin: 90 + lastWorkedWithinDays: 60, sorted by jss, maxItems: 200. 200 rows, $0.80. Ready-made: Top Rated US Python developers at $50–100/h.

A benchmark of what people charge

The same query without a badge filter, sort: "rate", 500 rows, then a pivot on country and member_since in the sheet. Rates are the listed rates; total_earnings and hours say who actually works at them.

A weekly refresh of a list you already have

Save the run as a task with changedSince: "7d" and includeStale: true, schedule it weekly: only freelancers whose rate, title, JSS, badge, hours, jobs, skills or availability moved come back, each with a changes list — nothing to diff. Ready-made: What changed this week.

What it costs

$4.00 per 1,000 stored rows, so a 500-row export is $2.00 and a 5,000-row one is $20.00. There is no start fee and a run that returns nothing costs nothing. liveSearch rows are $5.00 per 1,000; a fullProfiles read (the profile now, every contract, all the sections) is $20.00 per 1,000 profiles.

use it

Run the actor with your filters, then export the dataset in the format your sheet or pipeline wants.

moxlade/upwork-freelancers on Apify
$4.00 per 1,000 stored rows · $5.00 per 1,000 live search rows · $20.00 per 1,000 full profiles · no start fee, a zero-row run is free

Questions

Can I export Upwork freelancers to Excel?
Yes. The dataset's Export menu has Excel (xlsx) next to CSV and JSON, and the API takes format=xlsx. Arrays such as skills and contracts come through as JSON text in a cell.
Does the export include the freelancer's earnings?
Where Upwork exposes the figure, yes — total_earnings with earnings_precision: exact; where only a rounded band is shown, the band's lower bound with band; when hidden, null with earnings_hidden: true. A figure is on 47.7% of stored rows today.
How do I export a specific list of profiles rather than a search?
Pass profileUrls (profile URLs or ~01… ids, up to 5,000). Profiles that went private come back as status: private with the date; ids the census has not seen are listed in the run's status message.
Can the export include each freelancer's contracts?
Set include: ["contracts"] on any search or lookup and each row carries its completed contracts — title, type, dates, hours, charges, client name, the client's feedback with its six detail scores, and the freelancer's feedback about the client. For a complete, freshly read history use fullProfiles: true.
Is there a limit on how many rows I can export?
maxItems goes up to 5,000 per run; a stored search returns 500 rows per call and pages through the rest. Run it again with a different query or filters for more.