Upwork to Google Sheets, leads scored and logged
Upwork searching looks simple until you’re doing it every day. Tabs pile up, “maybe” jobs blur together, and you lose good leads because you saw them too late (or forgot where you saved them).
If you’re a freelancer chasing consistent work, you already know the grind. Agency owners tracking multiple categories feel it too, and consultants who need better-fit clients get hit hardest. This Upwork lead scoring automation filters the noise and highlights the opportunities worth replying to.
You’ll set up an n8n workflow that pulls jobs on a schedule, scores them with GPT-4, logs everything in Google Sheets, drafts proposals, and pings you in Telegram so you can act fast.
How This Automation Works
Here’s the complete workflow you’ll be setting up:
n8n Workflow Template: Upwork to Google Sheets, leads scored and logged
flowchart LR
subgraph sg0["Schedule Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "Schedule Trigger", pos: "b", h: 48 }
n1["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Run Apify Scraper"]
n2["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Get Dataset Items"]
n3@{ icon: "mdi:database", form: "rounded", label: "Read Existing Job IDs", pos: "b", h: 48 }
n4["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Filter Duplicates"]
n5@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Has New Jobs?", pos: "b", h: 48 }
n6["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Normalize Fields"]
n7@{ icon: "mdi:robot", form: "rounded", label: "AI Scoring", pos: "b", h: 48 }
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Parse AI Score"]
n9@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Filter Score = 60", pos: "b", h: 48 }
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items", pos: "b", h: 48 }
n11@{ icon: "mdi:robot", form: "rounded", label: "Generate Proposal", pos: "b", h: 48 }
n12@{ icon: "mdi:database", form: "rounded", label: "Log to Google Sheet", pos: "b", h: 48 }
n13@{ icon: "mdi:cog", form: "rounded", label: "Loop Complete", pos: "b", h: 48 }
n14["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Compute Metrics"]
n15["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>Send Summary"]
n16@{ icon: "mdi:cog", form: "rounded", label: "No New Jobs", pos: "b", h: 48 }
n7 --> n8
n5 --> n6
n5 --> n16
n13 --> n14
n8 --> n9
n14 --> n15
n10 --> n13
n10 --> n11
n6 --> n7
n0 --> n1
n4 --> n5
n11 --> n12
n2 --> n3
n1 --> n2
n9 --> n10
n12 --> n10
n3 --> n4
end
subgraph sg1["Error Flow"]
direction LR
n17@{ icon: "mdi:play-circle", form: "rounded", label: "Error Trigger", pos: "b", h: 48 }
n18["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>Send Error Alert"]
n17 --> n18
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0,n17 trigger
class n7,n11 ai
class n5,n9 decision
class n3,n12 database
class n1,n2 api
class n4,n6,n8,n14 code
classDef customIcon fill:none,stroke:none
class n1,n2,n4,n6,n8,n14,n15,n18 customIcon
Why This Matters: Upwork Search Creates Busywork
Manually hunting for Upwork jobs is deceptively expensive. You read the same vague briefs, open a bunch of “could be good” posts, then realize the budget is wrong or the client history is a mess. Meanwhile, the best projects get 20 proposals before you even notice them. It’s not just time. It’s the mental load of deciding what’s worth your attention, then trying to remember which leads you already reviewed so you don’t waste time re-checking them tomorrow.
The friction compounds. Here’s where it breaks down in real life.
- You end up scanning the same listings again because there’s no reliable “seen” system tied to a Job ID.
- Quality gets mixed with junk, which means you spend about an hour a day reading posts you’ll never apply to.
- Even when you find a strong lead, writing a tailored first draft takes long enough that you reply late.
- Without a simple log, it’s hard to spot patterns in what you win, what you lose, and what you should stop chasing.
What You’ll Build: Scored Upwork Leads + Draft Proposals in Sheets
This workflow runs automatically every 6 hours (you can change that). It uses an Apify scraper to fetch Upwork jobs that match your criteria, then compares them against the Job IDs already stored in your Google Sheet so you only review new opportunities. Next, GPT-4 evaluates each job for fit, client quality, and budget, and assigns a simple 0–100 score. Anything below your threshold gets dropped. Anything above it moves forward to proposal drafting, so you end up with a ready-to-edit draft and a clean log of what happened. Finally, Telegram sends you a summary notification, so you can jump on the best leads without living inside Upwork all day.
The workflow starts on a schedule, pulls listings, and removes duplicates. Then AI scores and filters the best jobs, drafts proposals for those, logs results into Google Sheets, and sends a Telegram recap so you know exactly what to review.
What You’re Building
| What Gets Automated | What You’ll Achieve |
|---|---|
|
|
Expected Results
Say you check Upwork 4 times a day and spend about 20 minutes each time scanning, opening tabs, and shortlisting. That’s roughly 80 minutes daily, and it still doesn’t produce a proposal draft. With this workflow running every 6 hours, you spend maybe 5 minutes reviewing the Telegram summary and your sheet, then edit the pre-written drafts for the few jobs scoring 60+. Most days, that’s about an hour back.
Before You Start
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets to store Job IDs, scores, and drafts.
- Apify for running an Upwork scraper actor.
- OpenAI API key (get it from the OpenAI API dashboard)
Skill level: Beginner. You’ll connect accounts, add a Sheet column, and paste a few IDs into environment variables.
Want someone to build this for you? Talk to an automation expert (free 15-minute consultation).
Step by Step
A schedule kicks everything off. n8n runs the workflow every 6 hours, so you’re not relying on “I’ll check later” willpower.
Upwork jobs are collected and cleaned up. Apify pulls listings that match your saved criteria, then the workflow loads your existing Job IDs from Google Sheets and removes anything you’ve already seen.
AI decides what’s worth your attention. GPT-4 evaluates fit, client quality, and budget, then the workflow extracts a 0–100 score and filters to keep only jobs scoring 60+.
Qualified jobs get drafted and logged. For each high-scoring listing, n8n generates a proposal draft, appends the details to your Google Sheet, and calculates run metrics (how many found, how many kept, and so on). Telegram sends you a summary, plus an error notice if something fails.
You can easily modify the scoring threshold (60+) to match how picky you want to be, and adjust the schedule if you want faster alerts. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Schedule Trigger
Set the workflow to run on a fixed schedule to kick off the job scraping cycle.
- Add and open Scheduled Automation Start.
- Set the schedule rule to run every 6 hours.
- Confirm the node connects to Launch Apify Crawl to start the scraping run.
Step 2: Connect Apify Crawling and Dataset Retrieval
Trigger the Apify actor and retrieve the dataset items for evaluation.
- Open Launch Apify Crawl and set Method to
POST. - Set URL to
=https://api.apify.com/v2/acts/{{ $env.APIFY_ACTOR_ID }}/runs?waitForFinish=300. - Enable Send Body and set JSON Body to the provided filter payload (hourly/fixed budgets, categories, limit 100).
- Credential Required: Connect your HTTP Header Auth credentials in Launch Apify Crawl.
- Open Retrieve Dataset Records and set URL to
=https://api.apify.com/v2/datasets/{{ $json.data.defaultDatasetId }}/items?clean=true. - Credential Required: Connect your HTTP Header Auth credentials in Retrieve Dataset Records.
APIFY_ACTOR_ID is set in your environment variables or the Apify URL will be invalid.Step 3: Connect Google Sheets and De-duplicate Jobs
Load existing job IDs from Google Sheets and remove already-seen listings before scoring.
- Open Load Stored Job IDs and set Sheet Name to
Upwork Jobs. - Set Document ID to
={{ $env.GOOGLE_SHEETS_DOC_ID }}. - Credential Required: Connect your Google Sheets credentials in Load Stored Job IDs.
- Review Remove Seen Listings to keep only jobs whose
uidis not found in the sheet’sJob IDcolumn. - Open Check For New Jobs and confirm the condition uses
={{ $json.uid }}with notEmpty logic. - Verify the false path routes to No New Roles Path for runs with no new jobs.
Job ID column in your sheet—missing or renamed columns will cause duplicates to pass through.Step 4: Set Up the AI Scoring Pipeline
Normalize job fields, score them with AI, and filter by high-quality opportunities.
- Open Standardize Job Fields and keep the code that maps raw job data into normalized fields like
jobId,title, andbudget. - Configure AI Fit Evaluation to use model
gpt-4o. - Credential Required: Connect your OpenAI credentials in AI Fit Evaluation.
- Confirm Extract Score Result parses the AI response JSON and maps
score,decision, andreasoning. - Open Filter High Scores and set the condition to
={{ $json.score }}gte60.
Step 5: Configure Proposal Drafting and Sheet Logging
Batch through qualified jobs, draft proposals, and append results to your log sheet.
- Open Iterate Qualified Jobs to split jobs into batches for sequential processing.
- Configure Draft Proposal Text with model
gpt-4o-minito generate proposal drafts. - Credential Required: Connect your OpenAI credentials in Draft Proposal Text.
- Open Append to Sheet Log and set Operation to
append. - Set Sheet Name to
Upwork Jobsand Document ID to={{ $env.GOOGLE_SHEETS_DOC_ID }}. - Credential Required: Connect your Google Sheets credentials in Append to Sheet Log.
- Verify the loop: Iterate Qualified Jobs outputs to Draft Proposal Text and then to Append to Sheet Log, which returns to Iterate Qualified Jobs until complete.
Step 6: Configure Run Metrics and Telegram Summary
Summarize the run and send a notification when processing is complete.
- Open Batch Loop End and ensure it connects to Calculate Run Metrics.
- Review Calculate Run Metrics to compute
scrapedandpassedcounts using data from Retrieve Dataset Records and Extract Score Result. - Open Dispatch Telegram Summary and set Text to
=✅ Upwork Scraper Done Scraped: {{ $json.scraped }} Passed: {{ $json.passed }}. - Set Chat ID to
={{ $env.TELEGRAM_CHAT_ID }}. - Credential Required: Connect your Telegram credentials in Dispatch Telegram Summary.
TELEGRAM_CHAT_ID isn’t set in your environment variables, the summary message will fail to deliver.Step 7: Add Error Handling
Capture workflow errors and notify your Telegram channel immediately.
- Open Error Catch Trigger to ensure it is ready to capture workflow errors.
- Configure Send Failure Notice with Text set to
=🚨 Error: {{ $json.error?.message || 'Unknown' }}. - Set Chat ID to
={{ $env.TELEGRAM_CHAT_ID }}. - Credential Required: Connect your Telegram credentials in Send Failure Notice.
Step 8: Test and Activate Your Workflow
Run a manual test to confirm each node executes correctly, then activate the schedule.
- Click Execute Workflow to trigger Scheduled Automation Start manually.
- Confirm Launch Apify Crawl and Retrieve Dataset Records return job data, and that Check For New Jobs routes appropriately.
- Verify high-scoring jobs pass through Filter High Scores and log in Append to Sheet Log with drafted proposals.
- Check that Dispatch Telegram Summary sends a summary with
scrapedandpassedcounts. - Toggle the workflow to Active to enable scheduled runs every 6 hours.
Troubleshooting Tips
- Google Sheets credentials can expire or need specific permissions. If things break, check the Google connection inside n8n’s Credentials panel first.
- Apify runs can take longer than expected depending on the dataset size. If “Retrieve Dataset Records” returns empty sometimes, rerun the workflow or add a longer wait before pulling results.
- Default prompts in AI nodes are generic. Add your niche, proof points, and “how I work” early or you will be editing outputs forever.
Quick Answers
About 15 minutes if your accounts are ready.
No. You’ll connect credentials and fill in a few IDs and variables inside n8n.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Apify + OpenAI usage, which is commonly about $0.50–3.00 per run for this workflow.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and you should. Most people start by changing the “runs every 6 hours” schedule, swapping the Apify actor to target different job categories, and adjusting the “60+” filter threshold. You can also rewrite the prompts in the AI Fit Evaluation and Draft Proposal Text nodes to match your niche (e-commerce, SaaS, design, whatever) and your tone. If you want different outputs, log extra columns in the Google Sheets “Append” step, like client spend, timezone, or a risk flag.
Usually it’s the chat ID or bot permissions. Confirm your TELEGRAM_CHAT_ID environment variable is correct, then check the Telegram credential in n8n and resend a test message. If the workflow can’t message you after an error, you lose the safety net, so it’s worth fixing first.
Plenty for a typical freelancer or small agency; the practical limit is your n8n plan and your Apify/OpenAI usage budget.
Often, yes, because this workflow benefits from branching, deduping logic, looping through items, and handling errors without paying extra per “path.” n8n also gives you the option to self-host, which can matter if you’re running it 4 times a day and logging lots of rows. Zapier or Make can still work if you only want “scrape → notify” and nothing else. But once you add scoring, filtering, drafting, and structured logging, n8n tends to feel less cramped. Talk to an automation expert if you want help choosing.
Set this up once, and your Upwork feed turns into a prioritized, searchable lead list with drafts attached. Honestly, that’s the difference between “I’ll apply later” and sending strong proposals while the job is still fresh.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.