Upwork API: 55 fields, no official endpoint

684 words ~3 min read Updated 2026-08-24

Look for an Upwork API and you will not find an official one. The data lives in Upwork's markup, so bulk access means scraping.

Here is what our own Upwork Actor returns, what it costs, and where its limits are.

Calling it as an API

Start a run and wait for the dataset with one call:

curl -X POST "https://api.apify.com/v2/acts/blackfalcondata~upwork-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H 'Content-Type: application/json' \
 -d '{"query": "..."}'

The Python equivalent:

from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("blackfalcondata~upwork-scraper").call(run_input={"query": "..."})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
 print(item)

What comes back

Every row contains 55 fields, grouped roughly as follows:

Below is an actual record from a run, trimmed after the leading fields:

{
 "jobId": "2091059219332908730",
 "title": "Prepare Me Daily Lists Of Domains Away To Be Deleted",
 "description": "I need someone who can prepare me daily lists of domains which are pending deletion, th...",
 "descriptionHtml": null,
 "descriptionMarkdown": "I need someone who can prepare me daily lists of domains which are pending deletion, th...",
 "contentHash": "cdacfd5b9adbc8ff3f0957008c209e1bd70d2b6a8daa212355cd30a3f8bc0071",
 "jobType": "HOURLY",
 "experienceLevel": "IntermediateLevel",
 "budgetAmount": null,
 "budgetCurrency": null,
 "hourlyBudgetMin": 8,
 "hourlyBudgetMax": 25,
 "weeklyRetainerBudget": null,
 "salaryMin": 8
}

What you give it

The input supports 55 options, including query, searchUrl, jobType, experienceLevel, workload, sort, category, location, excludeLocations, budget, and more. Filtering happens server-side. You pay for the records you request instead of collecting extra records and filtering them afterwards.

What it costs

There is no subscription. Pricing is per event:

These figures come from a measured run, not an estimate:

Limits

Questions people ask

Is there an official Upwork API? No. Upwork publishes this data on its pages but does not provide a public API for it. Every option here therefore reads those pages.

How do I authenticate? Use an Apify API token, either through ?token= or an Authorization: Bearer header. The token belongs to you, not us. It is the only credential involved, and no separate account on the source site is required.

How long does one call take? The measured run returned 25 records in 7.5 seconds. Because run-sync-get-dataset-items keeps the connection open for the full run, larger jobs should start the run first and then poll the dataset.

Can I page through results? Runs are bounded by maxAgeMinutes, not by paging through responses. You request a number of records and receive one dataset. To read that dataset in chunks, use the dataset endpoint's offset and limit parameters.

What format does it return? JSON is the default. By changing the format parameter, the same run can also be served from the dataset endpoint as CSV, XLSX and JSONL. No separate export step is needed.

Try it

a working Upwork API you can call today runs on Apify, and the endpoint above is the entire integration. New accounts receive $5 of free platform credit each month, enough to cover a real run of this size several times over.

Disclosure: we build and maintain this Actor, and the link above is an affiliate link.

Further reading

Also published on