Schema de ofertas de empleo: gana la SERP a InfoJobs

Cuando una empresa quiere que su propia vacante aparezca en Google antes que la misma oferta republicada en InfoJobs o LinkedIn, la primera pregunta que se hace es "qué schema necesito". Es la pregunta equivocada, o al menos la segunda. El schema JobPosting es un enriquecimiento: describe con precisión un dato que ya existe en una página. Si esa página no existe como URL propia, indexable y con contenido servido antes de ejecutar JavaScript, ningún marcado logrará que compita con un portal cuya única ventaja real es tener, para cada vacante, una URL que Google puede rastrear sin esfuerzo.
Este artículo cubre las dos capas por separado y en el orden correcto: primero la arquitectura que hace que una vacante sea indexable (una URL por oferta, no un listado que se rellena por fetch), y después el marcado JobPosting propiamente dicho, con las propiedades que Google exige de verdad y los motivos por los que una implementación técnicamente válida puede seguir sin generar ningún resultado enriquecido.
El cuello de botella no es el schema: es cómo se sirve la página
Muchas páginas de "trabaja con nosotros" son, en la práctica, una sola URL. /carreras o /empleo carga un listado de vacantes mediante una llamada a una API interna, y el usuario nunca navega a una URL distinta al hacer clic en una oferta: se abre un panel lateral, un modal o una vista que cambia el contenido sin cambiar la ruta. Desde el punto de vista de un visitante esto funciona perfectamente. Desde el punto de vista de un rastreador, existe una sola página, y esa página no contiene ninguna de las vacantes en el HTML que se sirve inicialmente.
Google puede ejecutar JavaScript, pero lo hace en una segunda pasada, con presupuesto de rastreo limitado y sin garantías de que cada estado dinámico de esa página se indexe como si fuera un documento distinto. Un JobPosting inyectado por JS en un contenedor que nunca tiene URL propia no es una página que pueda posicionar de forma independiente para "desarrollador backend en Madrid" ni para nada parecido: es un fragmento de una página que ya está compitiendo, sin ninguna ventaja, contra el listado completo de InfoJobs.
Una URL por vacante, con contenido servido en el HTML inicial
El requisito no es sofisticado, pero es el que casi nadie cumple: cada oferta necesita su propia URL, con su propio title, su propia meta description y su propio bloque de contenido visible sin depender de una interacción del usuario. /empleo/desarrollador-backend-madrid es una página. /carreras#job=482 no lo es: el fragmento después del almohadilla ni siquiera se envía al servidor, así que Google recibe siempre la misma respuesta sin importar qué oferta estuviera "abierta" cuando se generó el enlace.
Lo mismo aplica a los parámetros que dependen de estado de sesión o de un identificador que cambia en cada carga: si la URL de una vacante no es estable, no es indexable de forma fiable, aunque hoy resuelva al contenido correcto. Antes de tocar el schema, confirma esto con una prueba simple: desactiva JavaScript y carga la URL de una vacante concreta. Si el título, la descripción del puesto y la ubicación no aparecen en el HTML, ese es el problema que hay que resolver primero.
El título de la pestaña y la meta description también deberían ser propios de cada vacante: "Desarrollador Backend en Madrid" en lugar de "Trabaja con nosotros" repetido en cada oferta. Un title genérico compartido por veinte vacantes le da a Google veinte páginas indistinguibles entre sí para decidir cuál mostrar, y normalmente termina eligiendo ninguna antes que arriesgarse a mostrar la equivocada.
- Cada vacante vive en su propia ruta, estable y enlazable directamente, sin fragmento y sin parámetro de sesión.
- El contenido principal, título del puesto, descripción, ubicación y tipo de contrato, está en el HTML servido, no solo en el DOM tras ejecutar JavaScript.
- La página de listado enlaza a cada vacante con un enlace real, no solo con un manejador de clic en JavaScript.
- Existe un sitemap, o al menos rutas rastreables desde el listado, que expone las vacantes activas sin depender de que Google adivine las URLs.
Qué datos exige de verdad el schema JobPosting
Una vez que la vacante tiene una URL propia, el marcado JobPosting describe esa página en un formato que Google puede leer sin ambigüedad. Las propiedades que Google trata como imprescindibles para considerar la oferta elegible son pocas: title, description, datePosted, hiringOrganization y jobLocation, o, si el puesto es remoto, jobLocationType con el valor TELECOMMUTE junto con applicantLocationRequirements indicando desde qué países se puede aplicar.
description debe ser el texto real de la oferta, no un resumen genérico compartido entre varias vacantes: si diez ofertas distintas llevan la misma descripción de dos líneas sobre buen ambiente de trabajo sin detallar el puesto, Google puede tratarlas como contenido duplicado de baja calidad y ninguna termina mostrando el resultado enriquecido. employmentType y baseSalary no son obligatorios en todos los mercados, pero cuando se pueden declarar con datos reales, mejoran la elegibilidad: un salario inventado o una horquilla que no corresponde a la oferta real es peor que omitir el campo.
jobLocation necesita una dirección estructurada, calle, ciudad, región, código postal y país, dentro de un PostalAddress; no basta con mencionar la ciudad en el texto de la descripción. Este es uno de los errores más comunes: el campo existe en el JSON-LD pero con una ciudad genérica de la sede central, mientras la vacante real es para una oficina en otra ubicación.
datePosted también funciona como señal de frescura de cara al buscador de empleo: una oferta con una fecha de publicación antigua puede aparecer marcada como tal frente al candidato. La tentación de adelantar esa fecha cada semana para simular una vacante nueva es contraproducente: las directrices de Google para JobPosting son explícitas en que datePosted debe corresponder a la fecha real de publicación, y ajustarla artificialmente para parecer reciente es el tipo de manipulación que puede costar la elegibilidad de todo el feed de empleo del dominio, no solo de esa oferta.
Por qué "marca más empleo" se gana con arquitectura, no con marcado
La razón por la que InfoJobs o LinkedIn suelen ganar la búsqueda de una marca junto con la palabra empleo no es que tengan mejor schema. Es que cada oferta republicada en su plataforma vive en una URL indexable, con contenido servido de inmediato, en un dominio con autoridad acumulada durante años y con miles de páginas similares ya posicionadas para consultas parecidas. Tu propio dominio no necesita superar esa autoridad de golpe: necesita, primero, tener una página que compita en igualdad de condiciones de rastreo e indexación. El schema no le da esa capacidad a una página que Google ni siquiera puede leer; la mejora una vez que ya la tiene.
Esto también cambia el orden de prioridades cuando el tiempo es limitado. Si solo hay presupuesto para arreglar una cosa antes de la próxima ronda de contrataciones, migrar el listado de un modal en JavaScript a URLs individuales rastreables mueve más la aguja que perfeccionar cada propiedad opcional del JobPosting. El schema sin la página indexable no compite. La página indexable sin schema al menos puede posicionar de forma orgánica y, con el tiempo, optar a los resultados enriquecidos de empleo cuando se añada el marcado.
Vale la pena enlazar cada vacante desde al menos un punto navegable del sitio, el listado de empleo y, si aplica, la página del equipo o del departamento correspondiente, para que no dependa únicamente del sitemap para ser descubierta.
El ciclo de vida de una vacante: publicar, actualizar y cerrar
Una vacante no es una página estática que se publica una vez y se olvida. Google revisa activamente si un JobPosting sigue vigente, y penaliza, a nivel de elegibilidad para resultados enriquecidos y en casos repetidos también con acciones manuales sobre la propiedad completa, a los sitios que dejan ofertas cerradas mostrando datos estructurados como si siguieran abiertas.
validThrough es la propiedad que gestiona esto: debe reflejar la fecha real en la que la oferta deja de estar disponible, no una fecha arbitraria muy lejana en el futuro para no tener que tocarla. Cuando la vacante se cierra, hay dos caminos razonables: eliminar el JobPosting del HTML, devolviendo un código 404 o 410 si la página deja de tener sentido, o mantener la página con un mensaje claro de posición cubierta y sin el bloque de datos estructurados. Lo que no funciona es dejar el schema tal cual con una fecha de vigencia que ya pasó: Google puede dejar de confiar en el resto de las vacantes del mismo dominio si encuentra varias así.
Cuando se publica una vacante nueva, conviene tratarla como cualquier página nueva del sitio: enlazarla desde el listado el mismo día, confirmar que aparece en el sitemap y, si el volumen de contrataciones lo justifica, solicitar la indexación manualmente en lugar de esperar a que el rastreo regular la encuentre.
Errores frecuentes al implementar JobPosting
La mayoría de las implementaciones fallidas no fallan por un campo mal escrito en el JSON-LD, sino por decisiones que se tomaron en el equipo de producto o de recursos humanos sin pensar en cómo las lee un rastreador.
- El JobPosting se inyecta solo después de una interacción del usuario, como abrir un acordeón o un modal, y nunca llega a estar presente cuando Google renderiza la página.
- Todas las vacantes comparten la misma datePosted porque se migraron juntas desde un sistema anterior, lo que hace que Google trate a varias como duplicadas entre sí.
- jobLocation usa la dirección de la sede central para una vacante que en realidad es para una oficina o tienda distinta.
- Las vacantes remotas no declaran jobLocationType TELECOMMUTE ni applicantLocationRequirements, así que Google las evalúa como si necesitaran una ubicación física y las descarta.
- Las ofertas cerradas se quedan indexadas meses después, con validThrough vencido, sin que nadie revise el estado del feed de empleo.
- La descripción del JobPosting es un resumen corporativo genérico, distinto del texto real que ve el candidato en la página, lo que genera una discrepancia entre el dato estructurado y el contenido visible.
Cómo comprobar que la implementación funciona de verdad
La prueba de resultados enriquecidos de Google confirma que el JSON-LD tiene la sintaxis correcta y los campos obligatorios, pero no confirma que la página sea indexable ni que Google la haya rastreado con ese contenido presente desde el primer momento. Son dos comprobaciones distintas y conviene hacer ambas por separado.
Para la parte de arquitectura, inspecciona la URL de una vacante concreta en Search Console y revisa qué HTML vio Google en el último rastreo, no solo si el resultado final fue indexable. Para la parte del schema, el informe de mejoras sobre ofertas de empleo en Search Console muestra cuántas páginas tienen un JobPosting válido, cuántas tienen errores y, con el tiempo, si el número de vacantes elegibles se mantiene estable o se desploma cada vez que se actualiza el listado. Una caída brusca casi siempre indica que un despliegue reciente rompió la generación del marcado o la estructura de URLs, no que las vacantes hayan dejado de gustarle a Google de un día para otro.
Preguntas frecuentes
¿Necesito también schema de negocio local además del JobPosting?
No para las vacantes en sí. hiringOrganization dentro del JobPosting ya identifica a la empresa que contrata mediante su nombre y, si se declara, su URL. El schema de negocio local resuelve una necesidad distinta, la de una entidad física de cara al usuario, y es un marcado independiente que no sustituye ni es sustituido por JobPosting.
¿Puedo usar el mismo JobPosting para la misma oferta en varias ubicaciones?
Si la vacante es idéntica en varias oficinas, lo recomendable es una página, y un JobPosting, por ubicación, cada uno con su propio jobLocation. Publicar un solo bloque de datos estructurados con varias direcciones dentro de jobLocation no está soportado de forma fiable y suele derivar en que Google solo tenga en cuenta la primera.
¿Qué pasa si no declaro baseSalary?
La vacante sigue siendo elegible para resultados enriquecidos sin ese campo en la mayoría de los mercados. El efecto de omitirlo es que se pierde la posibilidad de mostrar el rango salarial directamente en el resultado, no la elegibilidad general del JobPosting. Si se declara, debe corresponder a la remuneración real ofrecida, no a una cifra estimada para completar el campo.
¿Cuánto tarda una vacante nueva en aparecer en los resultados de empleo de Google?
No hay un plazo fijo, y depende del rastreo normal del dominio salvo que se solicite indexación manual. Lo que sí es consistente es que una vacante servida en una URL indexable con el JobPosting completo desde el primer rastreo tiene muchas más probabilidades de aparecer en el primer ciclo que una que se corrige por partes después de publicarse.