Consultar las bolsas de trabajo a diario es tedioso. Este tutorial crea un flujo de trabajo n8n que busca ofertas de trabajo automáticamente, las filtra según sus criterios (ubicación, salario, estado remoto) y le envía un resumen diario por correo electrónico. Todo el flujo de trabajo se realiza sin código y utiliza el generador visual de n8n. El paso de búsqueda utiliza la API de Scavio a través del nodo de solicitud HTTP de n8n a $0,005 por búsqueda. Ejecutarlo dos veces al día cuesta alrededor de $0,30 al mes.
Requisitos previos
- n8n instalado (autohospedado o en la nube)
- Una clave API de Scavio de scavio.dev
- Una cuenta de correo electrónico para enviar resúmenes
Guia paso a paso
Paso 1: Configurar el nodo de solicitud HTTP n8n para búsqueda
Configure el nodo de solicitud HTTP para llamar a la API de búsqueda de Scavio. Este es el paso de búsqueda principal de su flujo de trabajo.
# 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"]}')Paso 2: Agregar lógica de filtrado de trabajos
Filtre los resultados de búsqueda para que coincidan con sus criterios. En n8n, utilice un nodo IF o un nodo Función para aplicar estos filtros.
# 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]}')Paso 3: Cree el resumen del correo electrónico
Formatee los trabajos filtrados en un resumen de correo electrónico HTML. En n8n, utilice el nodo Enviar correo electrónico con el cuerpo formateado.
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')Ejemplo en 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'])Ejemplo en 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']);Salida esperada
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