Upwork data API: jobs, proposals, Connects and contracts
Upwork's Android client signs in with an OAuth2 AccessToken, then exchanges a scoped session at /v1/auth/scoped-token. Find Work cards load from /v1/feeds/best-match and /v1/feeds/most-recent (ciphertext, hourlyBudget, skills.prefLabel); opening a card hydrates /v1/jobs/{jobId}/posting with the client's public company stats (paymentVerified, totalCharges).
Membership & Connects reads /v1/connects/wallet (pibStatus.availableConnects). Proposals page at /v1/proposals (applicationUID, chargeRate, connectsBid) and interview invitations at /v1/invitations. Inbox rooms are /v1/inbox/rooms, the Alerts bell is /v1/alerts/{userId}, and a contract room hydrates hourly charge_rate plus combinedTotalEarnings from /v1/contracts/{contractId}. Paths shown are an illustrative model of the data, not a published developer API.
Upwork (package com.upwork.android.apps.main, app version 2.14.0) is Upwork Global's Android client for both freelancers and clients: Find Work job cards, proposals and interview invitations, Connects / Proposal Invitation Bonus, contract rooms, messages and talent search. This page documents an illustrative data surface modelled on those screens. Sign-in yields an OAuth2 AccessToken (accessToken, refreshToken, expiresInSecs, tenantId) that /v1/auth/scoped-token upgrades for data calls. Find Work cards come from /v1/feeds/best-match and /v1/feeds/most-recent (ciphertext, hourlyBudget, skills.prefLabel); a job-detail client card loads at /v1/jobs/{jobId}/posting (paymentVerified, totalCharges). The Connects wallet is /v1/connects/wallet (pibStatus.availableConnects); proposals page at /v1/proposals (applicationUID, chargeRate, connectsBid) and invitations at /v1/invitations. Inbox, alerts and contracts live at /v1/inbox/rooms, /v1/alerts/{userId} and /v1/contracts/{contractId}. Calls carry the Bearer AccessToken, X-Upwork-Authentication and X-Upwork-API-TenantId.
Screenshots
API surface
Issue scoped session token
POST
/v1/auth/scoped-tokenosintExchanges the mobile session for a scoped token (accessToken, refreshToken, expiresInSecs, tenantId) required by feed, proposal and room calls that the cookie-exchange path otherwise issues.
Auth: Signed-in AccessToken plus session cookies. The server rejects a plain mobile Bearer when the scoped session-exchange is missing.
- accessToken
- refreshToken
- expiresInSecs
- tenantId
- grant_type
- scope
Illustrative example reconstructed from the app's interface — not a live capture.
POST /v1/auth/scoped-token HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId> Content-Type: application/json { "grant_type": "client_credentials", "scope": "workspace" }{ "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "refreshToken": "rt_7c2e91ab", "expiresInSecs": 3600, "tenantId": "1513535512629587969" }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the post-login handshake before Find Work hydratestoken fields match the session the rest of the screens reuse
Best-match job recommendations feed
POST
/v1/feeds/best-matchopendataPowers Find Work best-match cards: uid, ciphertext, title, hourlyBudget min/max, skills.prefLabel, connectPrice and the client's totalSpent / paymentVerificationStatus for the signed-in freelancer.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- results
- uid
- title
- ciphertext
- description
- type
- recno
- hourlyBudget
- min
- max
- skills
- prefLabel
- connectPrice
- client
- totalHires
- totalSpent
- paymentVerificationStatus
- publishedOn
- paging
- total
- count
Illustrative example reconstructed from the app's interface — not a live capture.
POST /v1/feeds/best-match HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId> Content-Type: application/json { "limit": 10, "offset": 0 }{ "results": [ { "uid": "1784869486235123712", "title": "Staff Product Manager", "ciphertext": "~015c7cd346853e24fd", "description": "Own the freelancer home feed.", "type": "HOURLY", "recno": 4123456789, "connectPrice": 16, "hourlyBudget": { "type": "RANGE", "min": 60, "max": 90 }, "skills": [ { "id": "1017484851352698921", "prefLabel": "Product Management" } ], "client": { "totalHires": 12, "totalSpent": 71401.96, "paymentVerificationStatus": "VERIFIED" }, "publishedOn": "2026-09-20T12:00:00.000Z" } ], "paging": { "total": 248, "count": 10 } }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Find Work Best Match tab cardsbudget range, skill chips and client spend/verified badge on each card
Most-recent job recommendations feed
POST
/v1/feeds/most-recentopendataLoads the Most Recent Find Work tab (id, ciphertext, publishedDateTime, title, type, recno, hourlyBudget) used when the freelancer switches off best-match ranking.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- results
- id
- uid
- ciphertext
- publishedOn
- title
- type
- recno
- hourlyBudget
- attrs
- skills
- prefLabel
- paging
Illustrative example reconstructed from the app's interface — not a live capture.
POST /v1/feeds/most-recent HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId> Content-Type: application/json { "limit": 10 }{ "results": [ { "id": "1784869486235123712", "uid": "1784869486235123712", "ciphertext": "~015c7cd346853e24fd", "publishedOn": "2026-09-21T08:15:00.000Z", "title": "Android contractor for wallet SDK", "type": "FIXED", "recno": 4123456790, "hourlyBudget": { "type": "RANGE", "min": 45, "max": 70 }, "attrs": [ { "id": "1017484851352698921", "prefLabel": "Android" } ] } ], "paging": { "total": 248, "count": 10 } }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Find Work Most Recent tabpublished time and budget range shown on each recency-sorted card
Hydrate marketplace job posting
GET
/v1/jobs/{jobId}/postingopendataHydrates a job-detail screen with the client's public company card: country, paymentVerified, totalContracts, totalJobsWithHires and totalCharges.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- id
- clientCompanyPublic
- country
- twoLetterAbbreviation
- timezone
- memberSinceDateTime
- paymentVerification
- paymentVerified
- workHistoryStats
- totalContracts
- totalJobsWithHires
- totalCharges
- currency
- displayValue
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/jobs/1802671287393773389/posting HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "id": "1802671287393773389", "clientCompanyPublic": { "id": "1009763084", "country": { "id": "922824128549781504", "name": "United States", "twoLetterAbbreviation": "US" }, "timezone": "America/Los_Angeles", "memberSinceDateTime": "2022-08-18T00:00:00.000Z", "paymentVerification": { "paymentVerified": true }, "workHistoryStats": { "totalContracts": 12, "totalJobsWithHires": 9, "totalCharges": { "currency": "USD", "displayValue": "71401.96" } } } }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the job-detail client cardpayment-verified badge and spend/hire stats on that card
Read Connects / invitation-bonus balance
GET
/v1/connects/walletopenfinanceReturns the freelancer's Proposal Invitation Bonus / Connects wallet (pibStatus.availableConnects, currentPrice, maxPrice, consumedConnectFraction) used by Membership & Connects.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- pibStatus
- personId
- active
- currentPrice
- maxPrice
- consumedConnectFraction
- availableConnects
- connectsBalance
- connectsBalanceFree
- connectsBalancePaid
- visibilityLevel
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/connects/wallet HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "pibStatus": { "personId": "1513535512629587968", "active": true, "currentPrice": 8, "maxPrice": 37, "consumedConnectFraction": 0.01, "availableConnects": 163 }, "user": { "freelancerProfile": { "userPreferences": { "visibilityLevel": 1 } } } }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Membership & Connects wallet tileavailableConnects and per-bid currentPrice shown on that tile
List proposals by type
POST
/v1/proposalsopendataPages the Proposals tab (active/submitted/archived): applicationUID, openingUID, chargeRate, connectsBid, coverLetter and occupationTitle for each application.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- paging
- total
- offset
- applications
- applicationUID
- vendorUID
- openingUID
- title
- status
- coverLetter
- terms
- chargeRate
- amount
- currency
- connectsBid
- duration
- occupationTitle
Illustrative example reconstructed from the app's interface — not a live capture.
POST /v1/proposals HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId> Content-Type: application/json { "type": "ACTIVE", "limit": 10 }{ "paging": { "total": 42, "offset": "eyJBY3RpdmUiOiIxMCJ9", "count": 10 }, "applications": [ { "applicationUID": "1784936978461474817", "vendorUID": "1513535512629587968", "openingUID": "1784869486235123712", "title": "Lead Paradigm Representative", "status": 7, "coverLetter": "I can start this week.", "terms": { "chargeRate": { "amount": "85", "currency": "USD" }, "connectsBid": 17, "duration": 0 }, "occupationTitle": "Product Management" } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Proposals tab's Active / Submitted / Archived pivotsrate, Connects spent and cover-letter preview on each row
List interview invitations
POST
/v1/invitationsopendataPages Invitation to interview rows (uid, jobPostingUid, status, invitationLetter, contractorUid, clientUid) that populate the Invitations tab.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- totalCount
- count
- invitations
- uid
- jobPostingUid
- title
- status
- statusId
- invitationLetter
- contractorUid
- clientUid
- ctime
Illustrative example reconstructed from the app's interface — not a live capture.
POST /v1/invitations HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId> Content-Type: application/json { "status": "PENDING", "pagination": { "offset": 0, "count": 10 } }{ "totalCount": 826, "count": 10, "invitations": [ { "uid": "1784872122316197888", "jobPostingUid": "1784872118568779776", "title": "Legacy Division Technician", "status": "Pending", "statusId": 0, "invitationLetter": "We would like to interview you this week.", "contractorUid": "1441159363691524096", "clientUid": "1783553054015062016", "ctime": "2026-04-29T09:07:29.000Z" } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Invitations tab rowsinvitation letter preview and pending status chip on each row
List saved jobs
GET
/v1/saved-jobsopendataReturns Saved jobs with followed/jobRid plus the posting's title, hourlyBudgetMin/Max and the client's paymentVerified / totalCharges card.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- personSavedJobs
- followed
- jobRid
- job
- id
- content
- title
- description
- contractTerms
- hourlyContractTerms
- hourlyBudgetType
- hourlyBudgetMin
- hourlyBudgetMax
- clientCompanyPublic
- paymentVerified
- workHistoryStats
- totalCharges
- rawValue
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/saved-jobs?limit=20&offset=0&followed=true HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "personSavedJobs": [ { "followed": true, "jobRid": "4123456789", "job": { "id": "1784869486235123712", "content": { "title": "Staff Product Manager", "description": "Own the freelancer home feed." }, "contractTerms": { "hourlyContractTerms": { "hourlyBudgetType": "RANGE", "hourlyBudgetMin": 60, "hourlyBudgetMax": 90 } }, "clientCompanyPublic": { "paymentVerification": { "paymentVerified": true }, "workHistoryStats": { "totalCharges": { "rawValue": 71401.96, "currency": "USD", "displayValue": "71401.96" }, "totalContracts": 12 } } } } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Saved jobs listfollow toggle, budget range and verified-client badge on each saved row
Recommended freelancers (talent search)
GET
/v1/talent/recommendedosintReturns ranked freelancer cards (id, ciphertext, hourlyRateAmount, occupationTitle, portraitUrl) that the client Talent search / recommended-talent strip hydrates.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- freelancers
- id
- name
- photoUrl
- profileUrl
- ciphertext
- hourlyRateAmount
- occupationTitle
- portraitUrl
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/talent/recommended?openingId=1784869486235123712&limit=10 HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "freelancers": [ { "id": "1939570823820670781", "name": "Ruthie L.", "photoUrl": "https://cdn.example.com/portraits/c1example", "profileUrl": "/freelancers/~01a4efc20a2cd41f48", "ciphertext": "~01a4efc20a2cd41f48", "hourlyRateAmount": 75, "occupationTitle": "Product Manager", "portraitUrl": "https://cdn.example.com/portraits/c1example" } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the recommended-talent strip on a jobhourly rate, occupation title and portrait on each card
List message rooms (simplified)
GET
/v1/inbox/roomsosintReturns the simplified room list (id, unreadStoriesCount, unreadMessageCount) that seeds the Messages tab before a full room payload is fetched.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- rooms
- id
- roomId
- title
- unreadStoriesCount
- unreadMessageCount
- unreadStories
- userId
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/inbox/rooms?userId=1513535512629587968 HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "rooms": [ { "id": "room_5a82d4931d5f5aea3817fd0a14fcbbba", "roomId": "room_5a82d4931d5f5aea3817fd0a14fcbbba", "title": "Staff Product Manager", "unreadStoriesCount": 2, "unreadMessageCount": 2, "unreadStories": true, "userId": "1513535512629587968" } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Messages tab conversation listunread badge counts on each room row
List room stories (simplified)
GET
/v1/inbox/rooms/{roomId}/storiesosintPages the simplified story (message) list for one room after the Messages tab opens a conversation.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- stories
- storyId
- roomId
- unreadStoriesCount
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/inbox/rooms/room_5a82d4931d5f5aea3817fd0a14fcbbba/stories HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "stories": [ { "storyId": "story_9f3a1c", "roomId": "room_5a82d4931d5f5aea3817fd0a14fcbbba", "unreadStoriesCount": 1 } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the in-room message thread after tapping a conversation
List user notifications
GET
/v1/alerts/{userId}osintPages the Alerts bell: notificationId, unread flags and title for job activity, invitations and room events.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- notifications
- notificationId
- unread
- notification_unread
- title
- userId
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/alerts/1513535512629587968 HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "notifications": [ { "notificationId": "n_9f3a1c", "unread": true, "notification_unread": true, "title": "Invitation to interview", "userId": "1513535512629587968" } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the Alerts bell dropdownunread flag and title on each alert row
Read organization notification counts
GET
/v1/alerts/org-countsosintReturns per-organization notification counters (organizations[].counters) that drive the Alerts and Messages badges.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- organizations
- id
- orgUid
- counters
- count
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/alerts/org-counts HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "organizations": [ { "id": "1513535512629587969", "orgUid": "1513535512629587969", "counters": [ { "id": "messages", "count": 2 } ] } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the org-switcher badge counts on Alerts and Messages
Fetch contract details
GET
/v1/contracts/{contractId}openfinanceLoads a Contract room: uid, referenceContractRid, hourly charge_rate and weekly_limit, plus combinedTotalEarnings used by Reports / earnings history.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- uid
- referenceContractRid
- state
- clientOrgUid
- clientTeamUid
- freelancerUid
- offerDetails
- title
- hourly
- charge_rate
- weekly_limit
- start_date
- fixedPrice
- combinedTotalEarnings
- currency
- displayValue
Illustrative example reconstructed from the app's interface — not a live capture.
GET /v1/contracts/1669484061874712576 HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId>{ "uid": "1669484061874712576", "referenceContractRid": "20799532", "state": 1, "clientOrgUid": "1638232740248182785", "clientTeamUid": "1638232740248182785", "freelancerUid": "1663144681572958208", "offerDetails": { "title": "Staff Product Manager", "description": "Hourly product contract", "hourly": { "charge_rate": "85", "weekly_limit": "40", "start_date": "2026-09-17T00:00:00.000Z" }, "fixedPrice": null }, "combinedTotalEarnings": { "currency": "USD", "displayValue": "12840.00" } }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the contract room header and earnings striphourly rate, weekly cap and lifetime earnings on that screen
List client contract offers
POST
/v1/hires/offersopenfinancePages the client's sent offers: offer id, title, state, type, contract.rid and freelancer.location.countryName for the Hires / Offers screens.
Auth: OAuth2 Bearer AccessToken issued after sign-in; X-Upwork-Authentication and X-Upwork-API-TenantId. GraphQL-style data calls additionally expect a scoped session-exchange token — a plain mobile Bearer is rejected when that scope is missing.
- totalCount
- edges
- node
- id
- startDateTime
- endDateTime
- title
- state
- type
- contract
- rid
- agency
- name
- freelancer
- location
- countryName
Illustrative example reconstructed from the app's interface — not a live capture.
POST /v1/hires/offers HTTP/1.1 Authorization: Bearer <accessToken> X-Upwork-Authentication: <accessToken> X-Upwork-API-TenantId: <tenantId> Content-Type: application/json { "pagination": { "offset": 0, "count": 10 } }{ "totalCount": 3, "edges": [ { "node": { "id": "101694088", "startDateTime": "2026-09-17T00:00:00.000Z", "endDateTime": null, "title": "Staff Product Manager", "state": "ACCEPTED", "type": "HOURLY", "contract": { "rid": "20799532" }, "agency": { "name": "Northwind Studio" }, "freelancer": { "location": { "countryName": "United States" } } } } ] }Derived from the app's interface; endpoint details are illustrative, not a live capture.
reconstructed from the client's Hires / Offers listoffer state chip, type and contract id on each row
Data categories
- job postings
- proposals
- interview invitations
- Connects balance
- contracts
- client company stats
- message rooms
- notifications
- freelancer profiles
Where teams use this data
Freelance talent CRM from job + proposal fields
A staffing CRM can poll POST /v1/proposals and POST /v1/invitations for applicationUID, openingUID, chargeRate, connectsBid and invitation status, then join each row to GET /v1/jobs/{jobId}/posting (paymentVerified, totalCharges, country) so recruiters see which clients actually spend.
Connects wallet and bid-cost tracking
A freelancer finance bot can read pibStatus (availableConnects, currentPrice, maxPrice, consumedConnectFraction) on GET /v1/connects/wallet and the connectsBid on each submitted proposal to forecast weekly Connects burn before the Membership & Connects screen renews.
Contract rate and earnings reconciliation
An agency ledger can pull GET /v1/contracts/{contractId} for hourly charge_rate, weekly_limit and combinedTotalEarnings, keyed by uid / referenceContractRid, and cross-check those figures against POST /v1/hires/offers on the client's Hires screen.
Client-side recommended-talent shortlist
A hiring desk can call GET /v1/talent/recommended for ciphertext, hourlyRateAmount, occupationTitle and portraitUrl, then open the matching simplified room on GET /v1/inbox/rooms when a conversation already exists.
Frequently asked questions
How does the Upwork Android app authenticate data calls?
Sign-in yields an OAuth2 AccessToken (accessToken, refreshToken, expiresInSecs, tenantId). Subsequent calls send Authorization: Bearer plus X-Upwork-Authentication and X-Upwork-API-TenantId. Feed, proposal and room hosts additionally expect a scoped token from POST /v1/auth/scoped-token — a plain mobile Bearer is rejected when that scope is missing.
Which data powers Find Work job cards?
POST /v1/feeds/best-match and POST /v1/feeds/most-recent return uid, ciphertext, title, hourlyBudget and skills.prefLabel. Opening a card hydrates GET /v1/jobs/{jobId}/posting, including paymentVerified and workHistoryStats.totalCharges on the client's public company card.
Where is the Connects / invitation-bonus balance exposed?
The Membership & Connects screens read GET /v1/connects/wallet: pibStatus.availableConnects, currentPrice, maxPrice and consumedConnectFraction. Submitted proposals also carry terms.connectsBid.
Can I read contracts and message rooms, not just jobs?
Yes. GET /v1/contracts/{contractId} returns uid, hourly charge_rate, weekly_limit and combinedTotalEarnings. GET /v1/inbox/rooms lists rooms with unreadStoriesCount, and GET /v1/alerts/{userId} pages the Alerts bell.
Topics
- Upwork data API
- Upwork job feed
- Upwork best-match jobs
- Upwork Connects wallet
- Upwork proposals list
- Upwork interview invitations
- Upwork contract earnings
- Upwork inbox rooms
- Upwork talent search
- Upwork AccessToken
Need this app's data API integrated?
We deliver scoped integrations for any named app — from USD 500 with source-code handoff, or hosted access billed per call. Tell us the data you need.
- NDA + SOW on every engagement
- Delivery in 3–7 days
- Payment only after acceptance
- Work scoped to authorized use