Upwork Clients Scraper — Company Behind Any Upwork Job
Pricing
from $50.00 / 1,000 named clients
Upwork Clients Scraper — Company Behind Any Upwork Job
Give it Upwork job IDs or URLs, from any Upwork scraper or the Upwork API. Get the company behind each job, the evidence for the name, what the client has paid, and each contract with the freelancer and both reviews. One price per named client. Jobs from the last 30 days.
Pricing
from $50.00 / 1,000 named clients
Rating
0.0
(0)
Developer
Moxlade
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 hours ago
Last modified
Categories
Share
The client behind an Upwork job. Give it Upwork job IDs or URLs, from any Upwork scraper or the Upwork API, and get the company that posted each job: its name and domain, the evidence for the name (the brands, people and clients the postings give away), what the client has paid on Upwork with a split by kind of work, and on request each contract with the freelancer, the money and both reviews. A client is named only at high confidence; every other ID comes back as a free row that says why. This is not Upwork's API and has no link to Upwork: it reads a corpus of public Upwork postings and client histories.
One job, its client and their contracts
Give it this:
{"jobIds": ["https://www.upwork.com/jobs/~022112651010648303714"],"includeContracts": true,"contractsPerBuyer": 3}
Back comes one row per job ID you give (recordType: job), and with includeContracts one row per contract of each named client (recordType: contract) — the fields below are the ones this real row carried; every row has the same 50, and a field the source did not show arrives as null, not as a zero:
{"recordType": "job","asked": "https://www.upwork.com/jobs/~022112651010648303714","upworkId": "022112651010648303714","found": true,"title": "Video Intro Designer","url": "https://www.upwork.com/jobs/~022112651010648303714","datePosted": "2026-10-20T21:03:51.152000+00:00","priceType": "Hourly","clientLocation": "United States","clientJobsPosted": 114,"clientHires": 86,"clientPublicSpend": 84893.26,"clientPaymentVerified": true,"buyerRef": "7cfdb2b6b69ad2760e3a9b47","identityKnown": true,"company": "TutorJack","domain": "tutorjack.com","confidence": "high","country": "United States","city": "Colorado Springs","spendOnRecord": 82971.57,"logoUrl": "https://www.google.com/s2/favicons?domain=tutorjack.com&sz=128","identity": {"relations": [{"type": "identity_candidate", "entityName": "TutorJack", "entityDomain": "tutorjack.com", "status": "confirmed", "confidence": "high", "evidence": "Named …,"contractsSummary": {"total": 48, "inProgress": 11, "completed": 37, "totalBilledUsd": 30108.41, "avgHourlyRate": 12.07, "maxHourlyRate": 73, "distinctFreelancers": 35, "avgRatingGiven": 5, …,"contractsAsOf": "2026-10-20T21:10:34.612361+00:00"}
The same actor, asked differently:
- A column of IDs pasted from a spreadsheet, named clients only up to 20 —
{"jobIdsText": "022112651010648303714\n~022112654213757398440\nhttps://www.upwork.com/jobs/~022112636033867914441", "maxItems": 20}
What a named row carries
The company, with the reason we believe it. company and domain are the client behind the job, given only at high confidence. identity lists the evidence: the brands the client operates, the people who sign the postings, the clients they work for, and the candidates we ruled out, each with its status, its confidence and the words in the postings that gave it away. Measured 2026-10-20 over the 4,933 clients named on postings from the last 30 days: identity is filled for 51% of them; it is empty when the name came straight from the record and needed no research.
What they have paid, before you write a proposal. contractsSummary covers the contracts we hold for the client: how many, how many still running, the total billed, the average and highest hourly rate, how many freelancers, the ratings they gave and the ratings freelancers gave them, and what the running contracts bill now. Filled for 99% of named clients (2026-10-20). Upwork shows about the last 50 contracts on a client's profile, so for a client with hundreds of hires the summary covers the recent ones, and totalBilledUsd can be far under clientPublicSpend, which is Upwork's lifetime figure.
Rate X-Ray: where the money went. contractsSummary.rateXray splits what the client paid by kind of work, one of Upwork's 63 subcategories, with the contracts, the spend, the share of spend and the median hourly rate in each. The kind of work is read from the contract title by a classifier, so a contract can land in a neighbouring subcategory; the amounts are exact either way. 99.9% of the contracts of named clients carry a label (2026-10-20), and a new contract gets its label within about an hour.
The reviews freelancers wrote about them. With includeContracts, each contract is a row: the job title, hourly or fixed, the months it ran, the rate, the total charged, the hours, the freelancer, the kind of work, and both reviews. ratingToClient and reviewToClient are what the freelancer said about the client, the nearest thing to a reference for a client you are about to work for.
How fresh the history is. contractsAsOf says when we last read the client's contract history. We read it again each time the client posts a job, and we answer only postings from the last 30 days, so it is rarely older than that: for 95% of named clients the history was read at their latest posting, median age 15 days (2026-10-20). A contract that ended or got a review after that date shows as it was then.
Every ID gets a row. An ID we do not hold comes back found: false, a job we hold but cannot name comes back identityKnown: false with a note, and a job posted more than 30 days ago says so. A gap in your list is something you see, not something you work out.
Be realistic about the hit rate. We hold 3.4 million postings back to March 2024. About 13% of recent postings resolve to a named company; the rest come back unresolved, free. That is high enough to run across a shortlist and far too low to treat as a directory. An ID that is unresolved today can be answered on a later run.
The second step of a pipeline, not a job search. It does not browse or search Upwork. Any Upwork job scraper gives you IDs; this turns them into clients. Paste the IDs as they come: a bare ID, a ~-prefixed ID, a job URL, a column of a spreadsheet or another Actor's url field all work.
All 50 fields of a row
Every row carries all of them. A field the source did not show arrives as null, never as a zero or an empty string, and the note says when that happens.
The job you asked about
| field | type | what it holds |
|---|---|---|
recordType | string | job for the row of an ID you gave, contract for a contract of a named client (with includeContracts). Join the two on upworkId. |
asked | string | Exactly what you gave for this ID, so the row matches your own list. |
upworkId | string | The job's Upwork ID, without the ~. Null on a row of an ID the run's time limit did not reach. |
found | boolean | True when we hold the job. False: not in the corpus, and the row says so in note. |
title | string | The job's title as posted. |
url | string | The job on Upwork. |
datePosted | string | When Upwork published the job (UTC). A client is named only for a job from the last 30 days. |
priceType | string | Hourly or Fixed, as the job states it. |
price | number | The fixed price, when the job gives one figure (USD). |
priceMin | number | Lower end of the hourly range (USD). |
priceMax | number | Upper end of the hourly range (USD). |
note | string | Why a row names nobody: not in the corpus, no high-confidence match, posted more than 30 days ago, or not reached by the run. |
The client as Upwork shows them
| field | type | what it holds |
|---|---|---|
clientLocation | string | The client's country as Upwork shows it on the job. |
clientJobsPosted | integer | Jobs the client has posted on Upwork, as the job shows it. |
clientHires | integer | Freelancers the client has hired, as the job shows it. |
clientPublicSpend | number | What Upwork shows as the client's lifetime spend (USD). |
clientPaymentVerified | boolean | Whether Upwork shows the client's payment method as verified. |
buyerRef | string | Our stable reference for the client, the same on every job of theirs and across runs. It is not an Upwork ID. |
The company behind the job
Filled only when identityKnown is true: a posting from the last 30 days whose client we name at high confidence.
| field | type | what it holds |
|---|---|---|
identityKnown | boolean | True when the row names the company: a job from the last 30 days and a match at high confidence. Only these rows are charged. |
confidence | string | How sure the match is (high on every named row). Null for a job older than 30 days. |
company | string | The company behind the job. Null unless identityKnown. |
domain | string | The company's web domain, when we hold one. |
country | string | Where the company is, from our research. |
city | string | The company's city, when we hold it. |
logoUrl | string | A logo or favicon of the company that loads in a browser or a sheet. 64% of named clients (2026-10-20). |
employees | string | Company size as the source states it, a count or a range. Text, about 3% of named clients. |
revenue | string | Company revenue as the source states it. Text, about 3% of named clients. |
industry | string | The company's industry, about 3% of named clients. |
founded | integer | The year the company was founded, about 3% of named clients. |
linkedinUrl | string | The company's LinkedIn page, about 3% of named clients. |
identity | object | The evidence for the name: relations (each with type, entityName, entityDomain, status, confidence, evidence: the brands, people and clients the postings give away) and ruledOut (candidates we rejected, with why). Null when the name needed no research. |
What the client has paid
| field | type | what it holds |
|---|---|---|
spendOnRecord | number | What the client has paid, as we hold it (USD). |
contractsSummary | object | What the client has paid over the contracts we hold: total, inProgress, completed, totalBilledUsd, avgHourlyRate, maxHourlyRate, distinctFreelancers, ratings given and received, what the running contracts bill (active…), asOf, and rateXray (spend by kind of work with the median rate). |
contractsAsOf | string | When we last read the client's contract history (UTC). It is read again each time the client posts a job. |
A contract row
Only with includeContracts, on rows with recordType: contract, most recent first, joined to the job row by upworkId and buyerRef.
| field | type | what it holds |
|---|---|---|
jobTitle | string | The contract's job title. |
jobType | string | hourly or fixed. |
status | string | in_progress or completed. |
startMonth | string | The month the contract started (YYYY-MM). |
endMonth | string | The month it ended (YYYY-MM); null while it runs. |
hourlyRate | number | The contract's hourly rate (USD); null on a fixed-price contract. |
totalCharge | number | What the contract billed in all (USD). |
totalHours | number | Hours billed; null on a fixed-price contract. |
freelancerName | string | The freelancer's name as Upwork shows it (first name, last initial). |
freelancerUpworkId | string | The freelancer's Upwork profile ID. |
subcategory | string | One of Upwork's subcategories, read from the contract title by a classifier. |
ratingToFreelancer | number | The rating the client gave the freelancer (1–5). |
reviewToFreelancer | string | What the client wrote about the freelancer. |
ratingToClient | number | The rating the freelancer gave the client (1–5). |
reviewToClient | string | What the freelancer wrote about the client. |
contractsHeldForBuyer | integer | How many contracts we hold for this client in all. |
Ways to run it
Paste the IDs you have. Put job IDs or URLs into jobIds, one per line, or paste a column into jobIdsText (newlines, commas or spaces). Both lists are joined and duplicates removed. Another Actor's output works too: jobUrls and startUrls take a list of URLs or of objects with a url or id.
Add the contracts. Turn on includeContracts: each named client adds its contracts as rows, most recent first, up to contractsPerBuyer (25 by default, 200 at most). A client asked about twice in one run gets its contracts once. Contract rows are free.
Cap the spend. maxItems caps the named clients, and so the charge. Rows that name nobody and contract rows do not count. Without it, the cap is the number of IDs you gave.
Run it after a job scraper. Schedule your Upwork job scraper, then run this on the IDs it returned, by an integration or a task. The job scraper tells you what is posted; this tells you who posted it.
Everything you can ask for
| input | accepts | default | what it changes |
|---|---|---|---|
jobIds | array | — | One per line. A bare id, a ~-prefixed id, or the job URL — all three work. |
jobIdsText | string | — | Paste a column straight from a spreadsheet or another Actor's output. Separated by newlines, commas or spaces — all fine. Combined with the list above, duplicates removed. |
includeContracts | boolean | false | For each client we can name, add their contracts: what the work was, who did it, what it paid, hours billed, and the reviews in both directions. Free. |
contractsPerBuyer | integer | 25 | Most recent first. Upwork's own profile shows the last 50; we hold more for a small number of heavy buyers. |
maxItems | integer | — | Caps the named clients and therefore spend. A row is charged only once it is in your dataset. Rows that name nobody and contract lines are free and not counted. Defaults to the number of ids you gave. |
If an input can't be used
The run still ends Succeeded and charges nothing. Its status message begins INPUT REJECTED and says what to change. The dataset holds a single row, {"error": true, "code": …, "message": …}, with no results, and the key-value record ERROR repeats it, so an agent should check error on the first row. A search that finds nothing is not an error, only an empty dataset. A run marked Failed is a fault on our side, not your input; it costs nothing and reaches us.
Before you run it
Is this Upwork's API? No. It has no link to Upwork. It answers from a corpus of public Upwork postings and the client histories Upwork shows to anyone, joined to the company behind them. You bring the IDs; it never searches or browses Upwork for you.
Why only jobs from the last 30 days? A client is named for a posting from the last 30 days only. An older posting comes back found: true with no company and a note that says so, free. The window keeps the answer about a client who is hiring now.
How sure is a name? A company is returned only at high confidence. Below that the row says identityKnown: false and why, and carries no company. An honest "not sure" costs you nothing; a guess shown as a fact could cost you a contract. identity shows the evidence for every name it gives.
Why does a contract have no rate, no hours or no review? Upwork does not show every number for every contract. Over the 184,946 contracts held for the clients named in the last 30 days (2026-10-20): startMonth and freelancerName 100%, totalCharge 91%, totalHours 63%, ratingToClient 56%, ratingToFreelancer 53%, hourlyRate 43%, reviewToClient 35%, reviewToFreelancer 31%. A fixed-price contract has no hours and no rate; a contract nobody reviewed has no rating. They arrive as null, not as zero.
How do I get the rows out? They land in the run's dataset: download them as JSON, CSV, Excel or XML from the run page, or read them over the Apify API. Save a run as a task, schedule it, and connect it to webhooks, Zapier, Make or Google Drive, or let an agent call the Actor over MCP. Join contract rows to their job row on upworkId, and the same client across runs on buyerRef.
Who makes this, and what else is there? Moxlade — corpora your agent can ask. Its other actors are the Upwork freelancer census, the Vinted sellers database, the Instagram email scraper, the Reddit scraper and the Idealista scraper.
What it costs
One price per named client, contracts included. You pay named_buyer once for each job ID answered with the company behind it, at high confidence. The evidence, the summary of what the client paid and, when you ask for them, the contract rows come with it at no extra charge. No start fee, no minimum, no platform usage billed to you, and an empty run is free. The price falls with your Apify plan.
| per named client | Free | Starter | Scale | Business |
|---|---|---|---|---|
Named client (named_buyer) | $0.10 | $0.085 | $0.07 | $0.05 |
Free: a job we do not hold, a job whose client we cannot name at high confidence, a job posted more than 30 days ago, an ID the run's time limit did not reach, and every contract row. A row is charged only once it is in your dataset. maxItems counts the named clients only, so it is your spending cap: 50 IDs with maxItems: 10 cost at most $1.00 on the Free plan. Enterprise plans have their own rates; the Actor's Pricing tab shows the price for your plan.
| event | what one event is | per 1,000 |
|---|---|---|
named_buyer | One Upwork job ID answered with the company behind it, at high confidence, with the evidence, the summary of what the client paid and, on request, the contract rows. Rows that name nobody and contract rows are free. Charged only once the row is in your dataset. | $100.00 |
Who asks this
Freelancers and agencies on Upwork — who the client is before you spend connects on a proposal: their company and site, what they pay per hour by kind of work, and what other freelancers wrote about working for them.
Sales teams that sell to Upwork clients — the companies behind the jobs your scraper already collects, with a domain to enrich and a spend history to rank them.
Agents and data pipelines — one call per batch of up to 5,000 IDs, JSON rows with a stable upworkId and buyerRef, a free row for every ID it cannot answer.
Questions
Every figure on this page was measured on our own runs or read from our own tables, on the date given next to it.