APIs de Bolsas de Empleo
Seis sistemas de seguimiento de candidatos publican las vacantes abiertas de una empresa a través de una API que ellos mismos documentan para uso público y sin autenticación. scrape_template lee esas APIs directamente: valores exactos, sin ningún LLM de por medio, y permitido por construcción en lugar de por argumentación.
1. Por qué la API de la propia plataforma supera al scraping de la bolsa
La página de empleo de una empresa es la representación de una base de datos. El sistema de seguimiento de candidatos que hay detrás — Greenhouse, Lever, Ashby, Workable, Recruitee, Teamtailor — es el sistema de registro en el que el empleador publica realmente, y los seis documentan un endpoint público que sirve esos mismos registros como JSON o RSS.
Leer ese endpoint no es una forma más barata de hacer scraping. Es una operación distinta con modos de fallo distintos: no hay marcado que interpretar mal, ni paginador que recorrer, ni modelo de lenguaje decidiendo qué significaba un campo.
La misma bolsa, de dos maneras
| Scraping de la bolsa renderizada | Lectura de la API de la plataforma | |
|---|---|---|
| Valores | Extraídos del marcado; un rediseño los cambia en silencio | Exactos, tal como los introdujo el empleador |
| Cobertura | Lo que la página renderice en el primer pintado | Todas las vacantes publicadas en la bolsa |
| Solicitudes | Una por página, más la paginación | Una solicitud para la bolsa, con paginación solo donde la plataforma pagina |
| Interpretación | Selectores o un LLM deciden qué significaba un campo | No se infiere nada; un campo ausente se lee como null |
| Permiso | Se argumenta caso por caso | La plataforma documenta el endpoint para uso público |
2. Los seis conectores de bolsas de empleo
Cada conector apunta a un endpoint que la propia plataforma documenta para uso público y sin autenticación, en un host cuyo robots.txt lo permite. Pase la URL de una bolsa y el conector resuelve por usted el endpoint de la API, o pase params con el identificador de la empresa en esa bolsa.
greenhouse-jobscontent: true.Token de la bolsa en job-boards.greenhouse.io/<token>
lever-postingsskip y limit.Empresa en jobs.lever.co/<company>
ashby-jobsNombre de la página de empleo en jobs.ashbyhq.com/<name>
workable-jobsdetails: true.Subdominio de la cuenta en apply.workable.com/<subdomain>
recruitee-offersSubdominio en <company>.recruitee.com
teamtailor-jobstt: para que sobrevivan la ubicación, el departamento, el rol y la división. Devuelve 100 vacantes salvo que per_page indique otra cosa.Subdominio en <company>.teamtailor.com
content: true añade la descripción HTML completa y lleva una bolsa grande de 349 KB a 4,2 MB (la bolsa de 571 vacantes de Stripe, medida el 2026-08-28). El details: true de Workable se comporta igual. Pida las descripciones cuando las necesite, no por costumbre.api.lever.co declara Crawl-delay: 1 en su robots.txt, así que lever-postings republica ese valor como crawlDelaySeconds: 1 para que lo respete la superficie que hace la solicitud. Los conectores nunca solicitan nada por su cuenta: construyen la URL y analizan la respuesta, lo que mantiene los tiempos de espera, la política SSRF y la limitación de velocidad en la superficie que los administra.3. Una sola forma de empleo en seis plataformas
Los seis conectores se normalizan sobre los mismos doce campos, de modo que dos bolsas se unen sin un paso de mapeo por fuente. Un campo que la plataforma no proporciona vuelve como null, nunca como una suposición verosímil.
La forma de empleo compartida
| Campo | Significado |
|---|---|
id | El identificador propio de la plataforma, convertido a texto para que una lista combinada tenga un solo tipo de identificador |
title | Título del puesto |
url | La vacante pública |
location | Ubicación tal como la indica la plataforma |
department | Departamento, cuando la plataforma tiene ese nivel |
team | Equipo, cuando la plataforma tiene ese nivel |
employment_type | Se transmite con las palabras de la propia plataforma, sin unificarlo en un único vocabulario |
remote | true, false o null cuando la palabra de la fuente no responde a la pregunta |
published_at | ISO 8601, normalizado desde seis formatos de fecha distintos |
updated_at | ISO 8601, cuando la plataforma publica uno |
description | Texto plano, con el HTML de la plataforma eliminado |
source | Qué conector produjo el registro |
- No se infiere nada. Greenhouse no publica ningún tipo de contrato, así que una vacante de Greenhouse informa
nullen ese campo en lugar de un seguroFull-time. - `remote` responde a una sola pregunta: ¿puede hacerse este trabajo desde cualquier lugar? El modelo híbrido es realmente parcialmente remoto, así que se lee como
nully la palabra original de la plataforma se conserva junto a él. Leer un puesto híbrido como remoto sería tan incorrecto como leerlo como presencial. - `employment_type` conserva la grafía de la plataforma. Decidir qué es realmente el
Regular Full Time (Salary)de Lever le corresponde a usted, que sí puede ver sus propios datos. - Los contactos de reclutamiento se descartan, no se mapean. Recruitee coloca un buzón de candidaturas por vacante en cada oferta; ese campo nunca llega a la salida. Una vacante es información de la empresa, y eso es todo lo que devuelven estos conectores.
Dos bolsas, una lista
Como la forma es compartida, combinar una bolsa de Greenhouse y una de Lever no requiere código de mapeo.
// Every job-board connector returns the same twelve fields, so two boards
// concatenate with no per-source mapping step.
const API = 'https://crawlforge.dev/api/v1/tools/scrape_template';
async function board(template: string, params: Record<string, unknown>) {
const response = await fetch(API, {
method: 'POST',
headers: {
'X-API-Key': process.env.CRAWLFORGE_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({ template, params }),
});
const payload = await response.json();
return payload.data.data.items;
}
const jobs = [
...(await board('greenhouse-jobs', { company: 'stripe' })),
...(await board('lever-postings', { company: 'matterport' })),
];
// One filter over both boards. `remote` is null wherever the platform's own
// word did not answer the question — hybrid is not remote and not on-site.
const remote = jobs.filter((j) => j.remote === true);
// Cost: 1 credit per call — 2 credits for both boards, however many jobs.4. Cómo invocarlo: params y auto
Un conector de bolsa de empleo necesita el identificador de la empresa en esa plataforma, que es una cadena corta dentro de la URL de la bolsa, no la URL completa. Páselo en params.
Cuando ya tiene una URL y prefiere no averiguar qué plantilla la gestiona, envíe template: "auto" y CrawlForge elige la plantilla a partir de la URL — de forma determinista, y con un patrón anclado a un host por delante de otro que solo coincide con una forma de ruta.
Los dos estilos de invocación
La misma bolsa, alcanzada de dos maneras.
# A job-board connector needs the company's identifier on that platform,
# not the whole careers-page URL. That is what `params` carries.
curl -X POST https://crawlforge.dev/api/v1/tools/scrape_template \
-H "X-API-Key: $CRAWLFORGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "greenhouse-jobs",
"params": {
"company": "stripe"
}
}'
# Add "content": true to the params for full descriptions — it takes a large
# board past 4 MB, so ask for it only when you need the text.{ "template": "list" } sin URL para obtener todas las plantillas con su identificador, su descripción y su mode: list para un conector que devuelve muchos registros en una sola llamada, entity para uno que devuelve un único registro.5. La misma idea, fuera de la contratación
Las bolsas de empleo son el caso más claro, pero el principio es la regla en todas partes: cuando un operador publica sus propios datos, se leen esos.
shopify-collectionproducts.json de la tienda — con el mismo precio exacto, precio comparativo y stock que shopify-product devuelve para un artículo, de modo que una colección y una página de producto no pueden contradecirse.nhtsa-vinnpi-provider6. Lo que no hacemos, y por qué
La lista siguiente no es una hoja de ruta. Cada entrada es un camino que consideramos, que técnicamente podríamos tomar y que descartamos por un motivo declarado.
Solo fuentes públicas y sin autenticación
smartrecruiters-postingsapi.smartrecruiters.com/robots.txt lo prohíbe todo para todos los agentes, con una única excepción para LinkedInBot (verificado el 2026-08-28). Alcanzar ese endpoint siendo cualquier otro significa anular robots.txt en cada llamada, y esa no es una decisión que un conector pueda tomar en su nombre. Queda fuera a la espera de un acuerdo con SmartRecruiters.robots.txt respetado — no lo anulamos
wday/cxs de Workday es el endpoint interno del propio sitio de empleo, no una API que Workday documente para uso público. Cualquier conector contra él queda condicionado a una declaración del cliente, y hoy no se publica ninguno.No es una API pública documentada
No se ofrece en ningún plan
npi-provider es una consulta a un registro precisamente por eso: un registro por NPI, sin nada más añadido.Datos de empresas, no de personas
Las reglas con las que se construyeron estos conectores
- Solo páginas y endpoints públicos y sin autenticación.
- Se prefiere una API documentada al scraping siempre que exista una.
robots.txtse respeta de forma predeterminada en todas las herramientas que hacen solicitudes. Anularlo es explícito, se hace por solicitud y queda registrado contra su API key — y nunca alcanza a un host de la lista de exclusión permanente de CrawlForge.- CrawlForge se identifica con honestidad: un único user agent, el nombre real del producto y una URL de contacto. Consulte la verificación del rastreador.
- Ritmos moderados de forma predeterminada, respetando
Crawl-delayyRetry-After. - Las exclusiones y las solicitudes de retirada se respetan de forma permanente, en la capa de plataforma.
scrape_template cuesta 1 credit por llamada, sea cual sea el conector que use y sin importar cuántas vacantes devuelva.