State of the Upwork
Continuing the work originally published by Aaron Melton at Ascend Automation Agency.

About

Credit

State of the Upwork continues work originally published by Aaron Melton at Ascend Automation Agency (May 2025 – April 2026). Aaron's a Maker School GOAT and we're carrying the legacy forward. Methodology, taxonomy, and report structure follow his open-source framework. AI-keyword tracking, AI-generated narrative, and the public CSV/JSON API are additions.

Methodology

  • Daily scrape of every automation-related Upwork job posting via the jupri/upwork Apify actor.
  • Keyword matching is substring-based on job title + description (case-insensitive). A job mentioning Zapier AND n8n counts in both buckets.
  • Average rate is dragged by outliers; we publish median and 25th/75th percentile rates alongside it.
  • Rate buckets: Ultra-Premium $150-500, Premium $100-150, Expert $75-100, Experienced $50-75, Mid-Tier $40-50, Entry $25-40.
  • Pre-May-2026 historical trend lines come from Aaron's published markdown tables; SOTU's own data starts May 2026.

Keywords tracked

Every job title and description is checked against this exact list. Substring match, case-insensitive. The SOTU additions are tracked starting May 2026; pre-May-2026 historical numbers don't have these.

Platforms Aaron's original

BucketMatch phrases (substring, case-insensitive)
Zapier
zapier
Make.com
make.commake comintegromat
n8n
n8n
Power Automate
power automatemicrosoft power automatems power automate

Applications Aaron's original

BucketMatch phrases (substring, case-insensitive)
HubSpot
hubspot
Airtable
airtable
Salesforce
salesforce
GoHighLevel
gohighlevelgo high levelhighlevel
Notion
notion
Monday.com
monday.commonday com
ClickUp
clickup
Asana
asana
Trello
trello
Jira
jira
Slack
slack
Google Sheets
google sheetsgoogle sheet
Excel
excelmicrosoft excel
QuickBooks
quickbooks
Xero
xero
Zoho
zoho

Generic CRM mentions Aaron's original

Counted as a separate "CRM demand" signal alongside dedicated CRM apps.

crmcustomer relationship management

AI tier Added by SOTU · new May 2026

Categories tracked starting May 2026 to give the AI-native automation segment its own trendline.

BucketMatch phrases (substring, case-insensitive)
Claude / Anthropic
claudeanthropic
OpenAI / GPT
openaigpt-4gpt-5gpt 4gpt 5chatgpt
AI Agents
ai agentautonomous agentagentic
RAG
ragretrieval augmentedretrieval-augmented
Vector DBs
pineconeweaviatechromaqdrantvector databasevector db
LangChain
langchainlanggraph
Voice AI
vapiretellelevenlabsvoice aivoice agent11labs
AI Builders
lindybardeenrelevance aigumloopcrewain8n ai
MCP
mcpmodel context protocol
Embeddings
embeddingembeddings

The keyword list is intentionally locked so month-over-month comparisons stay valid across the full archive.

Public data API

Three endpoints, all open (no auth, no rate limit, CORS open), all edge-cached for one hour.

GET/api/data.json

Latest published month as JSON. Add ?month=YYYY-MM for any past month.

GET/api/data.csv

Same data as a spreadsheet-friendly CSV (anonymized, no job IDs).

GET/rss.xml

RSS 2.0 feed announcing each new monthly publish — plug into Feedly, Reeder, etc.

Examples

Browser / curl
# Latest month
curl https://upwork.redwaterrev.com/api/data.json

# Specific month
curl 'https://upwork.redwaterrev.com/api/data.json?month=2026-04'

# Download CSV
curl -O https://upwork.redwaterrev.com/api/data.csv?month=2026-04
JavaScript (fetch)
// Latest month — runs from any browser, no auth needed
const r = await fetch('https://upwork.redwaterrev.com/api/data.json');
const month = await r.json();

console.log(`${month.total_jobs} jobs at avg $${month.avg_rate}/hr`);
console.log('Top platforms:', month.platforms);
console.log('AI tier:', month.ai_keywords);
Python (requests)
import requests

r = requests.get('https://upwork.redwaterrev.com/api/data.json',
                 params={'month': '2026-04'})
data = r.json()

print(f"{data['total_jobs']:,} jobs, avg ${data['avg_rate']}/hr")
for platform, count in sorted(data['platforms'].items(),
                              key=lambda x: -x[1]):
    print(f"  {platform}: {count}")
Pandas — quick chart
import pandas as pd

# Pull last 12 months into one DataFrame
months = ['2025-07','2025-08','2025-09','2025-10','2025-11','2025-12',
          '2026-01','2026-02','2026-03','2026-04','2026-05','2026-06']
rows = [pd.read_json(f'https://upwork.redwaterrev.com/api/data.json'
                     f'?month={m}', typ='series') for m in months]
df = pd.DataFrame(rows).set_index('month')

# Plot Zapier vs n8n vs Make.com over the year
platforms = pd.json_normalize(df['platforms']).set_index(df.index)
platforms[['Zapier','n8n','Make.com']].plot()

Response shape

The JSON response is a single object with these fields. Every counts-style field is a JSON object keyed by the canonical name shown in the keyword tables above.

  • month — "YYYY-MM"
  • total_jobs, avg_rate, median_rate, high_paying_count
  • platforms, applications, ai_keywords — objects of {name: count}
  • rate_buckets — counts per tier (ultra_premium, premium, expert, …)
  • high_paying_examples — array of top 20 $75+ jobs (title + rate + summary, anonymized)

Limitations

  • Jobs may mention multiple platforms (overlap exists).
  • Generic automation jobs without platform mentions are excluded from platform counts but still appear in totals.
  • Rates are posted maximums, not actual contracted rates.
  • Sample is limited to Upwork — not the broader freelance market.
  • Substring matching can catch incidental mentions (e.g. "excel" inside "excellent"). We accept the noise to maintain continuity with Aaron's taxonomy.

Bugs, ideas, suggestions

Best place is to DM Nick on Maker School. Or fill out the feedback form — it pings Nick directly with whatever you've got: bug report, feature request, data correction, whatever.

Run by Redwater Revenue.