# Changelog of Upwork Scraper - Freelance Job Listings with Client Intel (`blackfalcondata/upwork-scraper`) Actor

- **URL**: https://apify.com/blackfalcondata/upwork-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/blackfalcondata/upwork-scraper.md

## Changelog

### 0.6.81 — 2026-09-01

#### Fixed

- Restored full results. Upwork withdrew the interface this Actor used to authenticate, so
  from 31 August runs returned no jobs. Authentication now uses the route Upwork's own site
  uses, and runs deliver the complete field set again, including client country, total spent,
  payment-verified status, rating, review count and applicant count. No input changes are
  needed and saved tasks keep working as before. If you run in incremental mode, the first
  run after this fix delivers the jobs posted during the outage.
- A run that cannot reach Upwork at all now says so. When an interface this Actor depends
  on is withdrawn by Upwork, the run previously logged only "upstream temporarily
  unavailable", which reads as a passing blip that will fix itself. The log now names the
  real cause and states that it is not a problem with your input and that we are notified
  automatically. Brief upstream blips are unchanged, and the run still finishes rather
  than failing.

### Unreleased — 2026-08-27

#### Fixed

- Incremental mode now keeps your existing history wherever it can. A run started with a
  restricted API key previously moved to a fresh store unconditionally, which meant
  re-collecting everything once. It now uses your existing store whenever it is allowed to,
  and only starts a new one if access is genuinely refused.
- Updated the underlying crawler library. A single failed storage access could previously
  leave a run hanging until its time limit instead of failing or recovering.

### 0.6.6 — 2026-08-13

#### Fixed

- Incremental mode no longer fails with an unexplained error when it cannot open its saved state. Runs started with a restricted API key could not open the stored state that incremental mode depends on, and the run ended with a generic "please try again later" that never cleared however often it was retried. Such a run now stops with a specific message naming what to change, and incremental runs started this way use stored state the run is allowed to create, so incremental mode works rather than failing. Runs with `incrementalMode` off were never affected. Affected accounts rebuild their baseline once, which means one run of already-seen jobs.

### 0.6.5 — 2026-08-03

#### Fixed

- Runs no longer log `Attempting to charge for an unknown event 'detail-enrichment'`. Detail enrichment was listed as a separately billed $0.003 addon, but that event was never actually registered for this Actor, so the charge was rejected on every attempt and the warning was written into the run log instead. Nobody was ever billed for it. The addon has been withdrawn rather than activated: detail enrichment stays covered by the standard per-result fee, the pricing table no longer advertises a charge that does not exist, and no run's cost changes.

### 0.6.4 — 2026-08-02

#### Fixed

- A rejected `sessionToken` is no longer a silent downgrade. Previously, when Upwork rejected the supplied token, detail enrichment was switched off for the rest of the run and the only trace was a single warning line in the log — the run finished as a success with items that were quietly missing every detail-only field (screening questions, attachments, client industry, company size, activity, allowed countries, work history). The run now reports the rejection as an error, sets a run status message explaining which fields are absent and why, and records the condition in the run summary.
- Newest-first runs are now genuinely newest-first. Upwork's own recency ordering is only approximate — a job posted hours earlier can appear above a newer one — so results are re-sorted by publish time before they are written. The sort is applied before the `maxResults` cap, so a capped run keeps the newest jobs rather than the first ones Upwork happened to list. Runs that stream results in batches are ordered within each batch.
- Corrected all current guidance for obtaining the token, including the example task and detail-client diagnostics. The previously documented `oauth2_global_js_token` cookie carries a token issued by a different client that has no access to the detail fields. The value that works is the `authorization` request header on a signed-in upwork.com request. Values pasted with a leading `bearer ` or as a whole `name=value` pair are accepted and cleaned up automatically.

#### Fixed

- When the Upwork job search backend stays unavailable through every retry, the run now reports it as a temporary upstream fetch error instead of a generic unexpected failure, so retrying later is clearly the right next step.

### 0.6.2 — 2026-05-26

#### Fixed

- Brief network timeouts on the initial token request no longer fail the run outright. The retry layer now also recovers from transient thrown errors (timeouts, dropped connections), not just from non-success HTTP status codes.

### 0.6.1 — 2026-05-25

#### Fixed

- Upstream-transient GraphQL errors from the Upwork job search backend (e.g. brief "search failed" responses from the upstream search index) are now retried with backoff before failing the run. These previously surfaced as immediate run failures even when a retry seconds later would have succeeded.

### 0.5.x — 2026-04-26

**Notifications:**

- Added: WhatsApp (Meta Cloud API) — free-form messages within 24h service-conversation window
- Added: Generic JSON webhook (`webhookUrl` + optional `webhookHeaders`) — universal hook for n8n / Make / Zapier / custom HTTP backends
- Notification destinations now: Telegram + Discord + Slack + WhatsApp + Webhook = **5 channels**
- Standardized notification formatting and delivery across all supported channels.
- Run metadata in notifications now includes the human-readable search label.

### 0.5 — 2026-04-24

Deploy-hardening release.

#### Changed

- Renamed the enrichment billing event from `apify-actor-detail-enrichment`
  to `detail-enrichment` (event names with `apify-` prefix are reserved by
  Apify and cannot be registered on third-party actors).
- Input form titles now consistently start with an emoji prefix across all
  38 fields; schema auto-patches missing emojis on known field names on
  each pre-push run via the shared tooling.
- README regenerated from a fresh v0.5 sample so the live store page now
  shows the rich field set including client intelligence and enrichment.

#### Added

- Per-module test files: `apiClient`, `categories`, `detailClient`,
  `detailTransform`, `filters`, `notifications`, `regions`, `scoring` —
  brings total to 9 test files / 84 tests.
- Country-code normalization (`clientCountryCode` — always ISO-3) and full
  ISO-3 mapping for the countries Upwork emits.
- Budget currency defaults to `USD` when a budget amount is present (the
  source response never populates currency on search-surface results).

#### Fixed

- `clientCountry` canonicalized to full country name across responses so
  consumers can match consistently (Upwork mixes ISO-3 codes and names in
  roughly an 18/82 split).
- Country alias coverage for Upwork's non-standard spellings: Congo DRC,
  Puerto Rico, Palestinian Territories, Ivory Coast, Sierra Leone, Antigua
  and Barbuda, Gambia, Suriname, French Polynesia, Bermuda.
- Fixed authenticated detail enrichment failing before any detail fields
  could be returned.

### 0.4.0 — 2026-04-24

#### Added — notifications

Per-platform webhook/bot integrations, opt-in via separate input fields.
Fires in parallel after the dataset is pushed; failures on one platform
don't affect the others.

- **Telegram:** `telegramToken` + `telegramChatId`. Markdown-formatted
  messages with title, rate/budget, client country, applicant count, and
  link per job.
- **Discord:** `discordWebhookUrl`. Rich embeds color-coded by
  `customJobScore` (green for ≥4, blue otherwise), with fields for
  budget, experience level, client, proposals, and score.
- **Slack:** `slackWebhookUrl`. Block Kit formatting with linked titles
  and one-line metadata per job.
- `notificationLimit` caps per-batch dispatch (default 5, max 20).
- `includeRunMetadata` prepends a run summary line when true (default).

Pairs naturally with `incrementalMode` — only NEW/UPDATED jobs trigger
alerts, preventing duplicate notifications on recurring runs.

#### Added — README cookbook

Comprehensive user-facing `README.md` with minimal-start example, 7
use-case recipes (low-competition, geographic, URL-mode, real-time
alerts, LLM pipeline, custom DSL), full output sample with field
descriptions, session-token extraction guide, pricing table,
filter-reference tables, region groupings, and troubleshooting.

### 0.3.2 — 2026-04-24

Cookie-mode hardening after live empirical testing.

#### Fixed

- Authenticated detail enrichment no longer fails before any detail fields
  can be returned.

#### Added

- `sessionToken` input description now explicitly warns that the
  `oauth2v2_int_XXXX` format is rejected with permission errors — only JWT
  Bearer tokens from the `oauth2_global_js_token` cookie have detail-page
  scope. Live probes confirmed that OAuth2 clients issuing `oauth2v2_int_*`
  tokens are accepted as authenticated (HTTP 200) but lack scope for every
  field under `jobAuthDetails`.

### 0.3.1 — 2026-04-24

Data-quality patch, discovered during a 1000-item live audit.

#### Added

- `clientCountryCode` output field — canonical ISO-3 alpha code (USA, GBR,
  DEU, etc.) derived from Upwork's mixed country representation.
- `normalizeCountry()` + `deriveCountryCode()` helpers in `src/regions.ts`.
  Source data can contain either ISO-3 codes (~18% — ZAF, CHE, ARE, USA)
  or full country names (~82%). We now canonicalize `clientCountry`
  to the full name in all outputs, and always populate `clientCountryCode`
  with ISO-3 when resolvable.
- ISO-3 mapping table covering 100+ countries with alias support for
  Upwork's non-standard name spellings (Congo DRC, Puerto Rico, Palestinian
  Territories, Ivory Coast, etc.).

#### Fixed

- `budgetCurrency` was null in 100% of fixed-price jobs — Upwork's search
  source doesn't populate `isoCurrencyCode` on `fixedPriceAmount`. Now defaults
  to `"USD"` when a budget amount is present (Upwork payment layer handles
  FX, so search-surface budgets are always USD).

### 0.3.0 — 2026-04-24

Adds opt-in cookie-auth mode (`sessionToken` + `enrichDetails`). Users who
supply their own Upwork session token (`oauth2_global_js_token` cookie) unlock
a per-job detail fetch that enriches output with ~30 additional fields.

#### Added — enrichment output fields (when `enrichDetails: true`)

- **Client extras:** `clientCity`, `clientTimezone`, `clientCompanyName`,
  `clientCompanyId`, `clientMemberSince`, `clientIndustry`, `clientCompanySize`,
  `clientAvgHourlyRate`, `clientTotalAssignments`, `clientActiveAssignments`,
  `clientHoursBilled`, `clientTotalHires`, `clientOpenJobsCount`,
  `clientPostedJobsCount`, `clientIsTopClient`, `clientOpenJobs[]`.
- **Activity panel:** `activityTotalHired`, `activityInvitedToInterview`,
  `activityUnansweredInvites`, `activityInvitationsSent`,
  `activityLastBuyerActivity`.
- **Category:** `category`, `categoryGroup`.
- **Qualifications:** `allowedCountries`, `allowedRegions`, `allowedStates`,
  `allowedTimezones`, `allowedLanguages`, `prefEnglishSkill`,
  `minJobSuccessScore`, `minOdeskHours`, `risingTalentOnly`, `onSiteType`,
  `locationCheckRequired`.
- **Collections:** `questions[]`, `attachments[]`, `similarJobs[]`,
  `applicantsBidsStats`, `workHistory[]` with bi-directional feedback.
- **Annotation flags:** `isAIGeneratedDescription` (detects AI-written
  descriptions — unique quality signal), `postFlow` (e.g. RJP).
- **Provenance:** `enrichedAt`.

#### Added — inputs

- `sessionToken` (secret) — Bearer value from `oauth2_global_js_token` cookie.
- `enrichDetails` — opt-in boolean.
- Concurrency control for parallel detail fetches (default 5, max 30).

#### Added — pricing

- New PPE event `apify-actor-detail-enrichment` ($0.003/enriched job).
  Only charged when detail data is successfully returned. Paired with
  `incrementalMode`, only NEW/UPDATED/REAPPEARED jobs trigger the charge —
  UNCHANGED jobs serve their cached enrichment from state.

#### Added — state

- `JobStateEntry.enrichment` + `enrichedAt` persist enriched payloads across
  runs. UNCHANGED jobs automatically re-emit their cached enrichment.

#### Security

- Auth errors (401/403) gracefully disable enrichment for the rest of the
  run, emit a warning, and continue without charging.
- Detail enrichment avoids returning account-owner profile data in scraped
  output.

### 0.2.0 — 2026-04-24

Major release. Expands the search data surface with richer client intelligence.
Adds 14 filters, 5 post-fetch filter types, URL-mode input, and a composite
quality score.

#### Added — output fields (from authenticated search surface)

- `clientCountry`, `clientTotalSpent`, `clientSpentCurrency`, `clientPaymentVerified`, `clientRating`, `clientReviewCount`, `clientHasFinancialPrivacy` — full client-intelligence panel.
- `totalApplicants` — exact integer (not tier-buckets like `"20-49"`).
- `enterpriseJob`, `premium`, `personsToHire` — job-quality signals.
- `engagementType` (`FULL_TIME` / `PART_TIME` / `AS_NEEDED`), `engagementDuration` (label), `engagementDurationWeeks`.
- `budgetCurrency`, `weeklyRetainerBudget`, `createTime`, `sourcingTimestamp`.
- `skillsDetailed[]` with skill UIDs, parent UIDs, and highlighted flag.
- `customJobScore` — composite 0–5 score from verification + rating + spend + reviews. Formula documented in `src/scoring.ts`.

#### Added — filters

- `category` (name or UID, single or array) — 70+ Upwork categories mapped to `category2Uid`.
- `location` + `excludeLocations` with structured `{type: "COUNTRY"|"REGION", value}` format. Region expansion: `europe`, `north_america`, `south_america`, `asia`, `oceania`, `africa`, `middle_east`, `english_speaking`.
- `budget` (fixed-price range), `hourlyRate` (open or closed range).
- `duration` (`week` / `month` / `semester` / `ongoing`).
- `verifiedPaymentOnly` boolean, `proposals` range, `contractToHire` boolean.

#### Added — post-fetch filters

- `includeKeywords` / `excludeKeywords` — granular matching against title, description, and skills with independent boolean toggles.
- `fromDate` / `toDate` — publishTime range filter.
- `maxAgeMinutes` — only jobs posted within the last N minutes.
- `minClientTotalSpent`, `minClientRating`, `minClientReviewCount` — quality thresholds.
- `customFilters` — arbitrary rule DSL (`includes`, `notIncludes`, `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) applied to any output field.

#### Added — input modes

- `searchUrl` — paste an Upwork search URL; query params auto-parse into filter inputs. Explicit input fields override URL-parsed values.

#### Changed

- Compact output now includes the new client-intelligence fields and `customJobScore`.
- Example run inputs refreshed to showcase the new filter combinations.

### 0.1.1 — 2026-04-14

#### Added

- Cross-run repost detection (`isRepost`, `repostOfId`, `repostDetectedAt`).
- `skipReposts` input to exclude detected reposts from output.
- `contentHash` output field.
- `descriptionHtml`, `descriptionMarkdown` output fields.

#### Fixed

- `contentHash` now properly threaded into `classifyJob` and `detectRepostMatch` — repost detection previously no-op'd because hash was passed as null.

### 0.1.0 — 2026-04-04

Initial release.

- Keyword search with pagination (up to 5050 results per query).
- Filters: job type, experience level, workload, client hires, sort order.
- Incremental mode: new/updated jobs only, cross-run state via KV store.
- Compact output mode for AI-agent workflows.
- Description truncation via `descriptionMaxLength`.
