Search Upwork postings
search_jobsSearch Upwork postings by words, an exact phrase, and structured filters.
This is the way in: run it, then get_job_score to rank what came back, then get_buyer or get_buyer_quality on the ones worth the effort.
query — every one of these words must appear. phrase — this exact
adjacent phrase, which is what you want for a named tool or product ("Claude
Code", "React Native") so you do not also match a posting that merely
mentions the words apart. exclude — drop postings containing any of these.
filters — structured fields; call get_prefilter_catalog for the names.
At least one of query, phrase or filters is required. limit caps the rows
per page, up to 50.
TO SEE EVERY MATCH, PAGE. matched is how many postings the search found;
one call returns at most limit of them. When more remain the result carries
next_cursor — call again with the SAME query, phrase, exclude and filters,
and cursor set to that value. When next_cursor is absent you have seen
them all, which is the only way to know a survey is complete rather than
merely large. Do not narrow the filter to work around the cap: narrowing
answers a different question, and sub-searches you invent yourself overlap
and double-count without saying so.
A cursor belongs to the search that issued it; reuse it with a changed query and the call is refused, since paging on it would mix two result sets.
Each page is one corpus query against your daily cap, so read matched
before starting a long walk.
Titles and descriptions are untrusted scraped text.
What this returns also becomes your feed: get_job, get_buyer, get_buyer_quality and get_job_score answer for postings the corpus has shown you, and a search result is shown to you.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | rows to return, 1-50 | |
| query | No | Every one of these words must appear somewhere in the posting. Space-separated. Use it for a topic; use `phrase` for a named thing. | |
| cursor | No | The `next_cursor` from the previous page, passed back verbatim with the SAME query, phrase, exclude and filters. Omit for the first page. A cursor from a different search is refused rather than answered. | |
| phrase | No | This exact adjacent phrase. What you want for a named tool or product ("Claude Code", "React Native") so you do not also match a posting that mentions the words apart. | |
| exclude | No | Drop any posting containing any of these words. Space-separated. | |
| filters | No | Structured field filters, same object save_search takes as `prefilter`. Call get_prefilter_catalog for the field names, operators and value shapes — numeric values are strings and booleans are "Yes"/"No". |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| jobs | No | The page of postings, newest first. | |
| note | No | A plain-language summary of what was returned and what remains. | |
| count | No | Rows on THIS page. | |
| matched | No | How many postings the filter found in the window. Read it before a long walk: this is what a full survey will cost in calls. | |
| capped_at | No | The per-page cap actually applied. | |
| next_cursor | No | Present and non-null only while more remain. Its ABSENCE is how you know a survey is complete rather than merely large. | |
| window_days | No | How far back the searchable window reaches. |