Upwork Scraper performs a single authenticated scrape of Upwork Best Matches and stores job data in SQLite. The browser remains visible so Upwork verification or two-factor authentication can be completed manually when required.
SQLite is the application's only persistence format. The scraper does not create or export CSV files.
Upwork Scraper is designed to automate the process of scraping job postings from Upwork Best Matches. It utilizes Selenium for web scraping and interacts with the Upwork website to extract job details, including job titles, descriptions, and proposals. The script then stores the extracted data in a SQLite database for easy access and retrieval.
The script provides a streamlined solution for users who want to efficiently search for new job opportunities on Upwork without the hassle of manually browsing through job listings.
- Time Saving: Automates the process of scraping job postings from Upwork, saving users time and effort.
- Efficient Job Search: Facilitates a more efficient job search experience by automatically collecting and organizing job details.
- Scheduler Friendly: Each invocation performs one scrape and exits; use cron or systemd for recurring runs.
- Automated Scraping: Automatically scrolls through job postings on Upwork and extracts relevant job details.
- Database Integration: Stores job details in a SQLite database for easy access and retrieval.
This script is designed to automate the process of scraping job postings from Upwork in order to streamline the job searching experience. It aims to save time and effort for individuals who regularly check for new job opportunities on the site.
While the script is intended to be a helpful tool, users should be aware that scraping data from Upwork may potentially violate Upwork's terms of service or other legal agreements. It is important to use this script responsibly and in accordance with all applicable laws and regulations.
The author of this script disclaims any liability for the misuse or improper use of the script. Users assume all risks associated with its use, including any legal consequences that may arise from violating Upwork's terms of service.
By using this script, you agree to use it responsibly and to comply with all applicable laws and regulations. The author encourages users to review Upwork's terms of service and other legal agreements before using this script.
Use this script at your own discretion and risk.
- Python 3.11+
- uv
- Selenium
- Undetected Chromedriver
- SQLite3
- Google Chrome or Chromium
This program has been tested and verified to work correctly in Python 3.11.
Install uv and synchronize the locked environment:
uv sync --lockedUpgrading from the previous project layout? Read the migration guide first.
Run the scraper with:
uv run upwork-scraperFor maintainers and contributors, the Makefile is the canonical interface for local development:
make help # list available targets
make install # synchronize the locked uv environment
make check # run tests, lint, formatting, and type checks
make validate # validate local configuration and browser discovery
make validate-login # run the live login validationThe make hooks and make security targets are maintainer-only safeguards. Typical users
can use the uv run commands below without installing Gitleaks or pre-commit.
The legacy command remains supported:
uv run python upwork_best_matches_scraper.pyGitleaks and pre-commit are development safeguards, not runtime requirements. Typical users who only install and run Upwork Scraper can skip this section.
Maintainers and contributors should install the pre-commit hooks once per clone:
uv run pre-commit installThe hooks run Ruff and Gitleaks before each commit. Gitleaks scans staged changes for
credentials and other secrets. Install Gitleaks separately and ensure it is available on
your PATH; use the official Gitleaks releases
for installation. The project pins the hook definition to Gitleaks v8.30.1.
To run all hooks manually:
uv run pre-commit run --all-filesIf Gitleaks reports a real secret, stop and rotate it before committing. Do not bypass the hook unless the finding has been reviewed and is demonstrably a false positive.
Copy .env.example to .env and set the required credentials:
cp .env.example .env
.env must not be committed. For production or scheduled execution, environment variables may be supplied directly by the service manager instead.
UPWORK_USERNAME=your-email@example.com
UPWORK_PASSWORD=replace-meConfiguration
UPWORK_USERNAMEis required.UPWORK_PASSWORDis optional. Accounts connected to Google or Apple can leave it unset and complete authentication manually in the visible browser. The same manual flow supports Upwork password entry and two-step verification.UPWORK_FIRST_NAMEis required and must contain the first name shown in the Upwork profile panel. The current scraper uses it to isolate the job-results text before parsing.BROWSER_EXECUTABLE_PATHis optional. Without it, Google Chrome is preferred and Chromium is used as a fallback.UPWORK_PROXY_SERVERis optional and acceptshttp[s]://host:port,socks4://host:port,socks5://host:port, orhost:port(HTTP shorthand).UPWORK_DATABASE_PATH,UPWORK_DRIVER_CACHE_DIR,UPWORK_BROWSER_PROFILE_DIR,UPWORK_VERIFICATION_TIMEOUT, andLOG_LEVELare optional.- The browser profile is persistent by default, so cookies and completed Upwork verification can be reused on later runs. Do not use the profile directory concurrently from multiple scraper processes.
Upwork Scraper uses a dedicated persistent browser profile by default. On the first run, the visible browser may require your Upwork credentials, Google or Apple login, security verification, or two-step verification. Once authentication succeeds, later runs may reuse that session and open directly on Best Matches without displaying the login form.
If Upwork expires or invalidates the saved session, the login page will appear again and the normal
credential or manual verification flow will be used. The browser profile contains sensitive session
data: do not share it, commit it, or point UPWORK_BROWSER_PROFILE_DIR at your personal Chrome or
Chromium profile. Do not run multiple scraper processes against the same profile concurrently.
The browser major version is detected automatically. Users should not normally maintain a Chrome version list.
If Upwork reports network restrictions, configure the same stable proxy route that works in your normal browser. Avoid alternating between direct access and different proxy exit IPs. The scraper does not rotate proxies and does not accept credentials embedded in the proxy URL.
For example, an SSH SOCKS tunnel can be configured with:
ssh -N -D 1080 user@proxy-host
export UPWORK_PROXY_SERVER=socks5://127.0.0.1:1080Keep the tunnel and the scraper on the same stable route for the account’s session.
Validate configuration without starting a browser login:
uv run upwork-scraper --check-configValidate the live Upwork login without scraping or changing the jobs database:
uv run upwork-scraper --validate-loginThis opens the dedicated visible browser profile and waits for any Upwork security verification or two-factor authentication. For accounts connected through Google or Apple, complete the provider login in this browser window. It is a manual integration check and is not part of the automated test suite or CI.
The CLI also supports these overrides for one run:
--database PATHchanges the SQLite database location.--browser-path PATHselects a specific Chrome or Chromium executable.--proxy-server VALUEoverridesUPWORK_PROXY_SERVER.--verification-timeout SECONDSchanges the manual verification window.--log-level LEVELchanges console and file logging verbosity.
Run one scrape with the recommended command:
uv run upwork-scraperThe legacy entrypoint is also supported:
uv run python upwork_best_matches_scraper.pyUpwork Scraper performs the following tasks:
- Reuses an authenticated browser profile or goes to the Upwork login page and logs you in.
- Scrapes job postings from Upwork Best Matches page.
- Parses job details and stores them in a SQLite database.
During a scrape, progress logs report each scroll step, the number of visible job links, parsing counts, and periodic insert/update/unchanged/failure totals. The final message reports inserted, updated, unchanged, failed, and skipped jobs after the database commit succeeds. An updated job has a changed proposal value; an unchanged job was already stored with the same proposal value.
The scraper can automatically log in with native Upwork credentials when UPWORK_PASSWORD is
set. If the account was created or connected through Google or Apple, leave UPWORK_PASSWORD
unset and complete the provider login in the visible browser. Upwork two-step verification is
also completed manually when prompted. The browser profile is persistent, so successful provider
login and verification may be reused on later runs.
You can launch Upwork scraper via a bash script as a cron job.
Create a launch_upwork_scraper.sh file and put it inside a bin folder in the project directory.
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
parent="$(dirname "$DIR")"
# Run one scrape
cd "$parent"
nohup uv run upwork-scraper > /dev/null 2>&1 &
^ Edit the paths according to your virtual environment location.
Make the bash script executable:
chmod +x bin/launch_upwork_scraper.sh
Edit your crontab to schedule the execution of the bash script.
In the following example the scraper will be launched every 6 hours. So it should be run at minute 0 past every 6th hour. This means that the script will be run at 12:00 AM, 6:00 AM, 12:00 PM, and 6:00 PM every day
0 */6 * * * /path/to/your/UpworkScraper/bin/launch_upwork_scraper.sh
You might have to add the following variables inside your crontab in order to launch the Chromedriver. See here for more info.
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin
DISPLAY=:0
The values above are just examples. You need to replace them by finding your own values by issuing:
echo $SHELL
echo $PATH
env | grep "DISPLAY"
Contributions are welcome! If you encounter any issues or have suggestions for improvements, please open an issue or submit a pull request.
Copyright © 2026 roperi.
This project is licensed under the MIT License - see the LICENSE file for details.