Vérifier les sites d'emploi quotidiennement est fastidieux. Ce tutoriel construit un workflow n8n qui recherche automatiquement des offres d'emploi, les filtre selon vos critères (lieu, salaire, télétravail) et vous envoie un résumé quotidien par e-mail. L'ensemble du workflow est sans code grâce au constructeur visuel de n8n. L'étape de recherche utilise l'API Scavio via le nœud HTTP Request de n8n à 0,005 $ par recherche. En exécutant deux fois par jour, cela coûte environ 0,30 $/mois.
Prérequis
- n8n installé (auto-hébergé ou cloud)
- Une clé API Scavio depuis scavio.dev
- Un compte e-mail pour envoyer les résumés
Parcours
Étape 1: Configurer le nœud HTTP Request de n8n pour la recherche
Configurez le nœud HTTP Request pour appeler l'API de recherche Scavio. C'est l'étape centrale de recherche de votre 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"]}')Étape 2: Ajouter une logique de filtrage des offres
Filtrez les résultats de recherche selon vos critères. Dans n8n, utilisez un nœud IF ou un nœud Function pour appliquer ces filtres.
# 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]}')Étape 3: Construire le résumé par e-mail
Formatez les offres filtrées en un résumé HTML par e-mail. Dans n8n, utilisez le nœud Send Email avec le corps formaté.
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')Exemple Python
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'])Exemple JavaScript
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']);Sortie attendue
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