ScavioScavio
ProductoPreciosDocumentación
Iniciar sesionComenzar
  1. Inicio
  2. Tutoriales
  3. Crea un portal de empleo con una API de búsqueda (sin scrapear webs de empleo)
Tutorial

Crea un portal de empleo con una API de búsqueda (sin scrapear webs de empleo)

Crea un portal de empleo sin scrapear 100.000 webs de carrera. Usa la API de Google SERP para descubrir ofertas frescas en Greenhouse y Lever.

Obtener clave API gratisDocumentacion API

Para crear un portal de empleo sin scrapear miles de webs de carrera, usa una API de Google SERP como capa de descubrimiento: consulta en Google las ofertas recientes en los grandes dominios de seguimiento de candidatos (Greenhouse, Lever, Ashby, Workday), extrae de los resultados orgánicos el título, el enlace y el fragmento, elimina duplicados por URL y guarda lo que te interese. Nunca mantienes un solo scraper específico por web. Un desarrollador en r/developersIndia scrapeó más de 2 millones de ofertas de empleo de más de 100.000 webs de carrera en un único conjunto de datos, y un comentarista señaló el coste evidente: estaba "construyendo mi propia API de búsqueda de Google (que cuesta mucho)." Ahí está la trampa. Mantener 100.000 parsers se rompe cada vez que una página de empleo cambia su marcado, y una capa SERP casera devora proxies y horas de ingeniería. Google ya rastrea cada página pública de empleo; una consulta indexada saca a la luz las ofertas nuevas el mismo día en que aparecen. Este tutorial recorre POST /api/v1/google con operadores site: dirigidos y consultas por fecha, el parseo de organic_results, la eliminación de duplicados y el almacenamiento. Scavio cobra 2 créditos (0,01 $) por una llamada SERP completa y 1 crédito (0,005 $) por una ligera, con pago por uso real, sin mínimo mensual ni depósito mínimo. Alcance honesto: el descubrimiento por SERP te da amplitud y frescura a bajo coste, pero para campos plenamente estructurados (salario, ubicación normalizada, URL de aplicación) aún parseas la página de destino o usas una API de ATS donde exista. SERP es la capa de descubrimiento, no todo el pipeline de ETL.

Requisitos previos

  • Una clave de API de Scavio en scavio.dev (el plan gratuito incluye 50 créditos de un solo uso)
  • Python 3.9+ con la librería requests, o Node.js 18+ para la versión JS
  • Familiaridad básica con los operadores de Google (site:, intitle:, after:)
  • Un almacén para las ofertas (SQLite, Postgres o incluso un archivo JSON para empezar)

Guia paso a paso

Paso 1: Configura tu clave de API y elige las consultas de descubrimiento

Exporta tu clave como variable de entorno y diseña consultas que apunten a los dominios de seguimiento de candidatos donde publican la mayoría de empresas. Cada consulta combina un portal con un rol y, opcionalmente, una ubicación. Lanza varias consultas concretas en lugar de una amplia; la cobertura SERP por consulta es finita, así que segmentar por portal y rol saca más ofertas distintas.

Python
export SCAVIO_API_KEY="your_key_here"

# Discovery queries: board + role (+ optional location)
QUERIES = [
    'site:boards.greenhouse.io OR site:jobs.lever.co "backend engineer" remote',
    'site:jobs.ashbyhq.com "product designer"',
    'site:boards.greenhouse.io "data engineer" New York',
]

Paso 2: Llama a POST /api/v1/google y lee organic_results

Envía cada consulta al endpoint de Google con light_request=false para obtener el SERP completo. La respuesta incluye organic_results; cada fila trae position, title, link y snippet. El link es la oferta en vivo en Greenhouse o Lever, y el snippet suele contener el rol y la ubicación. Usa autenticación Bearer en la cabecera Authorization.

Python
import os, requests

H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}

def search(query):
    r = requests.post("https://api.scavio.dev/api/v1/google", headers=H,
        json={"query": query, "light_request": False})
    r.raise_for_status()
    return r.json().get("organic_results", [])

for row in search('site:jobs.lever.co "backend engineer" remote'):
    print(row["position"], row["title"], row["link"])

Paso 3: Elimina duplicados por URL y normaliza en registros de oferta

La misma oferta aparece en varias consultas, así que elimina duplicados por URL antes de guardar nada. Quita las cadenas de consulta y los parámetros de seguimiento para que dos URLs del mismo empleo se fusionen en una. Convierte cada resultado orgánico en un registro plano que controlas tú, conservando la consulta de origen para auditar la cobertura más adelante.

Python
from urllib.parse import urlsplit, urlunsplit

def canonical(url):
    p = urlsplit(url)
    return urlunsplit((p.scheme, p.netloc, p.path.rstrip("/"), "", ""))

def to_listing(row, source_query):
    return {
        "url": canonical(row["link"]),
        "title": row["title"],
        "snippet": row.get("snippet", ""),
        "source_query": source_query,
    }

seen = set()
listings = []
for q in QUERIES:
    for row in search(q):
        rec = to_listing(row, q)
        if rec["url"] not in seen:
            seen.add(rec["url"])
            listings.append(rec)

Paso 4: Mantenlo fresco y guárdalo en tu almacén

Añade una consulta por fecha para traer solo ofertas recientes y luego haz upsert por URL de forma programada (un cron diario basta para la mayoría de portales). Google indexa las páginas de empleo nuevas en horas o un día, así que un barrido diario mantiene el portal al día sin volver a scrapear nada. Aquí persistimos en SQLite; cambia a Postgres para producción.

Python
import sqlite3

db = sqlite3.connect("jobs.db")
db.execute("""CREATE TABLE IF NOT EXISTS listings (
    url TEXT PRIMARY KEY, title TEXT, snippet TEXT, source_query TEXT,
    first_seen TEXT DEFAULT CURRENT_TIMESTAMP)""")

# Fresh-only discovery query (last 7 days)
fresh = 'site:boards.greenhouse.io "software engineer" after:2026-06-13'
listings += [to_listing(r, fresh) for r in search(fresh)
             if canonical(r["link"]) not in seen]

for rec in listings:
    db.execute("""INSERT OR IGNORE INTO listings (url, title, snippet, source_query)
        VALUES (:url, :title, :snippet, :source_query)""", rec)
db.commit()
print(f"stored {len(listings)} listings")

Ejemplo en Python

Python
import os, requests, sqlite3
from urllib.parse import urlsplit, urlunsplit

H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}

QUERIES = [
    'site:boards.greenhouse.io OR site:jobs.lever.co "backend engineer" remote',
    'site:jobs.ashbyhq.com "product designer"',
    'site:boards.greenhouse.io "data engineer" New York',
]

def search(query):
    r = requests.post("https://api.scavio.dev/api/v1/google", headers=H,
        json={"query": query, "light_request": False})
    r.raise_for_status()
    return r.json().get("organic_results", [])

def canonical(url):
    p = urlsplit(url)
    return urlunsplit((p.scheme, p.netloc, p.path.rstrip("/"), "", ""))

db = sqlite3.connect("jobs.db")
db.execute("""CREATE TABLE IF NOT EXISTS listings (
    url TEXT PRIMARY KEY, title TEXT, snippet TEXT, source_query TEXT,
    first_seen TEXT DEFAULT CURRENT_TIMESTAMP)""")

seen, stored = set(), 0
for q in QUERIES:
    for row in search(q):
        url = canonical(row["link"])
        if url in seen:
            continue
        seen.add(url)
        db.execute("INSERT OR IGNORE INTO listings (url, title, snippet, source_query) VALUES (?, ?, ?, ?)",
                   (url, row["title"], row.get("snippet", ""), q))
        stored += 1
db.commit()
print(f"stored {stored} unique listings")

Ejemplo en JavaScript

JavaScript
import Database from "better-sqlite3";

const H = {
  Authorization: `Bearer ${process.env.SCAVIO_API_KEY}`,
  "Content-Type": "application/json",
};

const QUERIES = [
  'site:boards.greenhouse.io OR site:jobs.lever.co "backend engineer" remote',
  'site:jobs.ashbyhq.com "product designer"',
  'site:boards.greenhouse.io "data engineer" New York',
];

async function search(query) {
  const r = await fetch("https://api.scavio.dev/api/v1/google", {
    method: "POST",
    headers: H,
    body: JSON.stringify({ query, light_request: false }),
  });
  const data = await r.json();
  return data.organic_results ?? [];
}

function canonical(url) {
  const u = new URL(url);
  return `${u.protocol}//${u.host}${u.pathname.replace(/\/$/, "")}`;
}

const db = new Database("jobs.db");
db.exec(`CREATE TABLE IF NOT EXISTS listings (
  url TEXT PRIMARY KEY, title TEXT, snippet TEXT, source_query TEXT)`);
const insert = db.prepare("INSERT OR IGNORE INTO listings (url, title, snippet, source_query) VALUES (?, ?, ?, ?)");

const seen = new Set();
let stored = 0;
for (const q of QUERIES) {
  for (const row of await search(q)) {
    const url = canonical(row.link);
    if (seen.has(url)) continue;
    seen.add(url);
    insert.run(url, row.title, row.snippet ?? "", q);
    stored++;
  }
}
console.log(`stored ${stored} unique listings`);

Salida esperada

JSON
1 Senior Backend Engineer - Acme - jobs.lever.co https://jobs.lever.co/acme/8f2c...
2 Backend Engineer (Remote) - Globex https://jobs.lever.co/globex/1a9d...
3 Staff Backend Engineer - Initech https://jobs.lever.co/initech/4b7e...
stored 27 unique listings

Tutoriales relacionados

    Preguntas frecuentes

    La mayoria de los desarrolladores completan este tutorial en 15 a 30 minutos. Necesitaras una clave API de Scavio (el plan gratuito funciona) y un entorno de Python o JavaScript.

    Una clave de API de Scavio en scavio.dev (el plan gratuito incluye 50 créditos de un solo uso). Python 3.9+ con la librería requests, o Node.js 18+ para la versión JS. Familiaridad básica con los operadores de Google (site:, intitle:, after:). Un almacén para las ofertas (SQLite, Postgres o incluso un archivo JSON para empezar). Una clave API de Scavio te da 50 creditos gratuitos al registrarte.

    Si. El plan gratuito incluye 50 creditos al registrarte, mas que suficiente para completar este tutorial y crear un prototipo funcional.

    Scavio tiene un paquete nativo de LangChain (langchain-scavio), un servidor MCP y una API REST simple que funciona con cualquier cliente HTTP. Este tutorial usa the raw REST API, pero puedes adaptarlo al framework que prefieras.

    Recursos relacionados

    Best Of

    La mejor API SERP basada en colas en 2026

    Read more
    Best Of

    La mejor API de búsqueda de Google en 2026

    Read more
    Solution

    Datos de Google Ads de las API SERP

    Read more
    Glossary

    API de SERP

    Read more
    Glossary

    SERP estructurado frente a Raw Scrape

    Read more
    Comparison

    Google CSE (Paid Tier) vs Third-Party SERP API (Scavio, SerpApi, Serper)

    Read more

    Empieza a construir

    Crea un portal de empleo sin scrapear 100.000 webs de carrera. Usa la API de Google SERP para descubrir ofertas frescas en Greenhouse y Lever.

    Obtener clave API gratuitaLeer la documentacion
    ScavioScavio

    API de busqueda en tiempo real para agentes de IA. Busca en todas las plataformas, no solo en Google.

    Producto

    • Funciones
    • Precios
    • Panel
    • Afiliados

    Desarrolladores

    • Documentacion
    • Referencia de API
    • Inicio rapido
    • Integracion MCP
    • Python SDK

    Alternativas

    • Alternativa a Tavily
    • Alternativa a SerpAPI
    • Alternativa a Firecrawl
    • Alternativa a Exa

    Herramientas

    • Formateador JSON
    • cURL a codigo
    • Contador de tokens
    • Todas las herramientas

    © 2026 Scavio. Todos los derechos reservados.

    Featured on TAAFT
    Terminos de servicioPolitica de privacidad