Checking job boards daily is tedious. This tutorial builds an n8n workflow that searches for job listings automatically, filters them by your criteria (location, salary, remote status), and sends you a daily email digest. The entire workflow is no-code using n8n's visual builder. The search step uses the Scavio API via n8n's HTTP Request node at $0.005 per search. Running twice daily costs about $0.30/month.
Prerequisites
- n8n installed (self-hosted or cloud)
- A Scavio API key from scavio.dev
- An email account for sending digests
Walkthrough
Step 1: Configure the n8n HTTP Request node for search
Set up the HTTP Request node to call the Scavio search API. This is the core search step of your workflow.
# n8n HTTP Request Node Configuration:
# Method: POST
# URL: https://api.scavio.dev/api/v1/search
# Authentication: None (we use header auth)
# Headers:
# x-api-key: {{$env.SCAVIO_API_KEY}}
# Content-Type: application/json
# Body (JSON):
# {
# "query": "software engineer remote jobs 2026",
# "country_code": "us",
# "num_results": 10
# }
# Python equivalent for testing outside n8n:
import requests, os
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def search_jobs(query: str) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': 10})
return resp.json().get('organic_results', [])
results = search_jobs('software engineer remote jobs 2026')
for r in results[:3]:
print(f'{r["title"][:50]}: {r["link"]}')Step 2: Add job filtering logic
Filter search results to match your criteria. In n8n, use an IF node or Function node to apply these filters.
# n8n Function Node - Filter Jobs
# This code runs inside n8n's Function node
# For Python testing:
def filter_jobs(results: list, criteria: dict) -> list:
filtered = []
for r in results:
text = (r.get('title', '') + ' ' + r.get('snippet', '')).lower()
# Check required keywords
has_required = all(kw.lower() in text for kw in criteria.get('required_keywords', []))
# Check excluded keywords
has_excluded = any(kw.lower() in text for kw in criteria.get('excluded_keywords', []))
# Check salary mentions
has_salary = any(s in text for s in ['$', 'salary', 'compensation', '/yr', '/year'])
if has_required and not has_excluded:
filtered.append({
'title': r['title'],
'url': r['link'],
'snippet': r.get('snippet', ''),
'has_salary_info': has_salary
})
return filtered
criteria = {
'required_keywords': ['remote', 'engineer'],
'excluded_keywords': ['senior staff', 'principal', 'intern'],
}
filtered = filter_jobs(results, criteria)
print(f'{len(filtered)}/{len(results)} jobs match criteria')
for j in filtered:
print(f' {j["title"][:50]}')Step 3: Build the email digest
Format filtered jobs into an HTML email digest. In n8n, use the Send Email node with the formatted body.
from datetime import datetime
def build_digest(jobs: list, query: str) -> str:
date = datetime.now().strftime('%B %d, %Y')
html = f'<h2>Job Search Digest - {date}</h2>\n'
html += f'<p>Search: <em>{query}</em> | Found: {len(jobs)} matches</p>\n'
html += '<hr>\n'
for i, job in enumerate(jobs, 1):
html += f'<h3>{i}. <a href="{job["url"]}">{job["title"]}</a></h3>\n'
html += f'<p>{job["snippet"][:200]}</p>\n'
if job.get('has_salary_info'):
html += '<p><strong>Has salary information</strong></p>\n'
html += '<hr>\n'
html += f'<p><small>Cost: $0.005 | Powered by Scavio Search API</small></p>'
return html
# n8n Cron Trigger: Run at 8am and 6pm daily
# n8n Schedule: 0 8,18 * * *
digest = build_digest(filtered, 'software engineer remote jobs 2026')
print(f'Digest generated: {len(digest)} chars')
print(f'Daily cost: 2 searches = $0.01')
print(f'Monthly cost: ~$0.30')Python Example
import requests, os
from datetime import datetime
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def job_search_digest(query, required=None, excluded=None):
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': 10})
jobs = []
for r in resp.json().get('organic_results', []):
text = (r['title'] + ' ' + r.get('snippet', '')).lower()
if required and not all(k.lower() in text for k in required):
continue
if excluded and any(k.lower() in text for k in excluded):
continue
jobs.append({'title': r['title'], 'url': r['link']})
print(f'Job Digest - {datetime.now().strftime("%B %d")}:')
for j in jobs:
print(f' {j["title"][:50]}: {j["url"]}')
return jobs
job_search_digest('remote python developer jobs 2026', required=['remote', 'python'])JavaScript Example
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function jobDigest(query, required = [], excluded = []) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us', num_results: 10 })
});
const results = (await resp.json()).organic_results || [];
const jobs = results.filter(r => {
const text = (r.title + ' ' + (r.snippet || '')).toLowerCase();
return required.every(k => text.includes(k.toLowerCase()))
&& !excluded.some(k => text.includes(k.toLowerCase()));
});
jobs.forEach(j => console.log(`${j.title.slice(0, 50)}: ${j.link}`));
return jobs;
}
jobDigest('remote python developer jobs 2026', ['remote', 'python']);Expected Output
software engineer remote jobs 2026: https://...
Remote Software Engineer - TechCorp: https://...
Senior Remote Engineer at StartupX: https://...
5/10 jobs match criteria
Remote Software Engineer - TechCorp
Full Stack Engineer - Remote at ScaleUp
Digest generated: 1240 chars
Daily cost: 2 searches = $0.01
Monthly cost: ~$0.30