From bee3a395eb6898ed8c5e8c67c156d109807b8727 Mon Sep 17 00:00:00 2001 From: John Cole <29712567+johnisanerd@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:04:39 -0400 Subject: [PATCH] Add apify-isolved-jobs-api skill --- .claude-plugin/marketplace.json | 29 ++- skills/apify-isolved-jobs-api/SKILL.md | 172 ++++++++++++++++++ .../references/actor-index.md | 15 ++ .../references/gotchas.md | 31 ++++ 4 files changed, 240 insertions(+), 7 deletions(-) create mode 100644 skills/apify-isolved-jobs-api/SKILL.md create mode 100644 skills/apify-isolved-jobs-api/references/actor-index.md create mode 100644 skills/apify-isolved-jobs-api/references/gotchas.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d672d18..fa3fcfe 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,7 +13,7 @@ "name": "apify-ads-intelligence", "source": "./skills/apify-ads-intelligence", "skills": "./", - "description": "Research, spy on, and analyze ads across Meta (Facebook & Instagram), Google (Ads Transparency Center + paid search results), TikTok (Ads Library + Creative Center), LinkedIn Ad Library, and X (Twitter — promoted tweets, best-effort) using Apify Actors. Use when user asks about competitor ads, ad library research, winning creatives, ad copy analysis, landing page audits from ads, cross-platform ad audits, brand transparency checks, or any task involving paid ad creatives, advertiser data, or ad targeting from public ad libraries.", + "description": "Research, spy on, and analyze ads across Meta (Facebook & Instagram), Google (Ads Transparency Center + paid search results), TikTok (Ads Library + Creative Center), LinkedIn Ad Library, and X (Twitter \u2014 promoted tweets, best-effort) using Apify Actors. Use when user asks about competitor ads, ad library research, winning creatives, ad copy analysis, landing page audits from ads, cross-platform ad audits, brand transparency checks, or any task involving paid ad creatives, advertiser data, or ad targeting from public ad libraries.", "keywords": [ "ads", "advertising", @@ -97,7 +97,7 @@ "name": "apify-buying-signal-detection", "source": "./skills/apify-buying-signal-detection", "skills": "./", - "description": "Set up a recurring buying-signal detection pipeline that finds companies showing buying intent across three signal types — job postings (hiring for the persona), fundraising events (recent raises), and LinkedIn content (pain-point posts, hiring announcements) — then aggregates results into a deduplicated leads.csv with the signal source, evidence URL, and detection timestamp per row. Split-schedule architecture — Apify Actor Tasks pull raw data on their own cadence, a Claude-side aggregation task normalizes, deduplicates against a blacklist, and appends new leads with a weekly-idempotent guard. Use when the user says \"find companies with buying signals\", \"detect intent signals for outbound\", \"set up a weekly lead pipeline\", \"monitor hiring signals for lead gen\", \"track startup funding leads\", \"find LinkedIn buying signals\", \"schedule Apify Actors for prospecting\", \"build a signals-based lead list\", or \"set up buying-intent monitoring for my ICP\".", + "description": "Set up a recurring buying-signal detection pipeline that finds companies showing buying intent across three signal types \u2014 job postings (hiring for the persona), fundraising events (recent raises), and LinkedIn content (pain-point posts, hiring announcements) \u2014 then aggregates results into a deduplicated leads.csv with the signal source, evidence URL, and detection timestamp per row. Split-schedule architecture \u2014 Apify Actor Tasks pull raw data on their own cadence, a Claude-side aggregation task normalizes, deduplicates against a blacklist, and appends new leads with a weekly-idempotent guard. Use when the user says \"find companies with buying signals\", \"detect intent signals for outbound\", \"set up a weekly lead pipeline\", \"monitor hiring signals for lead gen\", \"track startup funding leads\", \"find LinkedIn buying signals\", \"schedule Apify Actors for prospecting\", \"build a signals-based lead list\", or \"set up buying-intent monitoring for my ICP\".", "keywords": [ "buying-signals", "intent-data", @@ -191,7 +191,7 @@ "name": "apify-influencer-brand-collabs", "source": "./skills/apify-influencer-brand-collabs", "skills": "./", - "description": "Discover Instagram brand–creator partnerships by chaining Apify Actors. Use when the user asks who collabs with a brand, which brands a creator has done paid posts for, wants to audit an influencer's branded-content history, or wants to scope a brand's sponsorship roster. **Triggers:** - \"who collabs with [brand] on Instagram?\" - \"what brands has [creator] done sponsored posts for?\" - \"find paid partnerships / branded content for [handle]\" - \"audit [influencer]'s brand deals\" - \"show me [brand]'s influencer roster\" Works in either direction — brand → creators or creator → brands — and detects direction from the data, so don't ask the user to declare it. Requires Apify MCP tools.", + "description": "Discover Instagram brand\u2013creator partnerships by chaining Apify Actors. Use when the user asks who collabs with a brand, which brands a creator has done paid posts for, wants to audit an influencer's branded-content history, or wants to scope a brand's sponsorship roster. **Triggers:** - \"who collabs with [brand] on Instagram?\" - \"what brands has [creator] done sponsored posts for?\" - \"find paid partnerships / branded content for [handle]\" - \"audit [influencer]'s brand deals\" - \"show me [brand]'s influencer roster\" Works in either direction \u2014 brand \u2192 creators or creator \u2192 brands \u2014 and detects direction from the data, so don't ask the user to declare it. Requires Apify MCP tools.", "keywords": [ "influencer", "brand", @@ -209,7 +209,7 @@ "name": "apify-lead-scoring-enrichment", "source": "./skills/apify-lead-scoring-enrichment", "skills": "./", - "description": "Score and enrich a CSV of B2B leads using Apify Actors. Takes a CSV with company URLs, free-text scoring rules, and an enrichment preference; runs BuiltWith (tech stack), Website Content Crawler (content classification), and Contact Info Scraper (company metadata) for scoring; enriches with either department-specific contacts (Contact Info Scraper + Bulk Email Finder fallback) or copywriter discovery (Google Search Scraper → AI Web Scraper → Bulk Email Finder). Outputs an enriched CSV with a numeric score and a per-lead outreach_hook column that personalizes cold email copy (e.g. \"uses Shopify → send Shopify install guide\"). Use when user asks to score leads, qualify leads, enrich a lead list, detect a company's tech stack for outreach, find marketing/sales/engineering contacts at a list of companies, hunt down blog copywriters for guest-post pitches, personalize cold email at scale, or turn a raw domain list into a ready-to-pitch account list.", + "description": "Score and enrich a CSV of B2B leads using Apify Actors. Takes a CSV with company URLs, free-text scoring rules, and an enrichment preference; runs BuiltWith (tech stack), Website Content Crawler (content classification), and Contact Info Scraper (company metadata) for scoring; enriches with either department-specific contacts (Contact Info Scraper + Bulk Email Finder fallback) or copywriter discovery (Google Search Scraper \u2192 AI Web Scraper \u2192 Bulk Email Finder). Outputs an enriched CSV with a numeric score and a per-lead outreach_hook column that personalizes cold email copy (e.g. \"uses Shopify \u2192 send Shopify install guide\"). Use when user asks to score leads, qualify leads, enrich a lead list, detect a company's tech stack for outreach, find marketing/sales/engineering contacts at a list of companies, hunt down blog copywriters for guest-post pitches, personalize cold email at scale, or turn a raw domain list into a ready-to-pitch account list.", "keywords": [ "lead-scoring", "lead-enrichment", @@ -231,7 +231,7 @@ "name": "apify-link-prospecting-outreach", "source": "./skills/apify-link-prospecting-outreach", "skills": "./", - "description": "Find sites ranking for target keywords, score every prospect with Ahrefs domain authority and page-level traffic, identify the strongest pitch angle per row (\"links to competitor\", \"mentions brand without linking\", \"top-3 SERP\", \"resource page\", \"outdated content\"), generate brand-voice-matched outreach emails using an outreach-type-aware template (unlinked-mention claim, competitor-link replacement, resource-page inclusion, outdated-content replacement, topical niche-edit), and propose a concrete in-article link placement as three artifacts — the verbatim source sentence, the same sentence rewritten with the link spliced in, or a fully-drafted new insertion if no natural fit exists. Use when user asks to find link building opportunities, prospect link partners, recover unlinked brand mentions, replace competitor links, build a tiered outreach list, or run cold email outreach for SEO link building.", + "description": "Find sites ranking for target keywords, score every prospect with Ahrefs domain authority and page-level traffic, identify the strongest pitch angle per row (\"links to competitor\", \"mentions brand without linking\", \"top-3 SERP\", \"resource page\", \"outdated content\"), generate brand-voice-matched outreach emails using an outreach-type-aware template (unlinked-mention claim, competitor-link replacement, resource-page inclusion, outdated-content replacement, topical niche-edit), and propose a concrete in-article link placement as three artifacts \u2014 the verbatim source sentence, the same sentence rewritten with the link spliced in, or a fully-drafted new insertion if no natural fit exists. Use when user asks to find link building opportunities, prospect link partners, recover unlinked brand mentions, replace competitor links, build a tiered outreach list, or run cold email outreach for SEO link building.", "keywords": [ "link-building", "seo", @@ -295,7 +295,7 @@ "name": "apify-verified-email-finder", "source": "./skills/apify-verified-email-finder", "skills": "./", - "description": "Builds a list of verified business emails from Google Maps, Google SERPs, or a user-supplied URL list. Verification happens inside the same Apify run — no third-party verifier needed. Use when user asks to find verified emails, build a leads list, scrape emails from Maps or SERP, verify emails for a URL list, or find an Apollo / Hunter alternative.", + "description": "Builds a list of verified business emails from Google Maps, Google SERPs, or a user-supplied URL list. Verification happens inside the same Apify run \u2014 no third-party verifier needed. Use when user asks to find verified emails, build a leads list, scrape emails from Maps or SERP, verify emails for a URL list, or find an Apollo / Hunter alternative.", "keywords": [ "email", "verification", @@ -314,7 +314,7 @@ "name": "apify-x402-agentic-wallet", "source": "./skills/apify-x402-agentic-wallet", "skills": "./", - "description": "Discover, pay for, and run any Apify Actor by paying USDC on Base over the x402 protocol with a Coinbase Agentic Wallet (awal) — no Apify account or API key. You buy one small, spend-capped prepaid Apify token, then run as many Actors as the request needs with it. Use when the user wants to use Apify tools without signing up, pay per use with crypto / USDC, set up an agentic wallet, mentions \"x402\", \"awal\", \"agentic wallet\", \"Coinbase wallet\", \"pay with USDC\", \"no API key\", or asks to pull live web data (social media, search, maps, marketplaces, news) while paying on-chain per use.", + "description": "Discover, pay for, and run any Apify Actor by paying USDC on Base over the x402 protocol with a Coinbase Agentic Wallet (awal) \u2014 no Apify account or API key. You buy one small, spend-capped prepaid Apify token, then run as many Actors as the request needs with it. Use when the user wants to use Apify tools without signing up, pay per use with crypto / USDC, set up an agentic wallet, mentions \"x402\", \"awal\", \"agentic wallet\", \"Coinbase wallet\", \"pay with USDC\", \"no API key\", or asks to pull live web data (social media, search, maps, marketplaces, news) while paying on-chain per use.", "keywords": [ "x402", "awal", @@ -330,6 +330,21 @@ "web-data" ], "category": "data-extraction" + }, + { + "name": "apify-isolved-jobs-api", + "source": "./skills/apify-isolved-jobs-api", + "skills": "./", + "description": "Scrape isolved and ApplicantPro jobs from any isolvedhire.com career site as structured rows, with a real new-or-changed feed from each posting's lastmod and the description as Markdown.", + "keywords": [ + "apify", + "isolved", + "jobs", + "ats", + "mcp" + ], + "category": "data-extraction", + "version": "1.0.0" } ] } diff --git a/skills/apify-isolved-jobs-api/SKILL.md b/skills/apify-isolved-jobs-api/SKILL.md new file mode 100644 index 0000000..c75f9d4 --- /dev/null +++ b/skills/apify-isolved-jobs-api/SKILL.md @@ -0,0 +1,172 @@ +--- +name: apify-isolved-jobs-api +description: "Scrape isolved jobs from any isolvedhire.com career site with the isolved Jobs API Actor (johnvc/isolved-jobs-api). Returns live job rows as JSON: title, hiring organization, tenant, employment type, locations with derived country, posted date, a sitemap last-modified stamp, apply URL, the employer's published salary when present, and the description as Markdown by default. Takes tenant slugs, tenant URLs, or single job URLs, and resolves legacy ApplicantPro sites too; filters by title, location, employment type, and salary before billing. updatedAfter turns a daily schedule into a real new-or-changed-postings feed using each job's own lastmod, checked before any page is fetched, so you pay only for what moved. Use when someone wants isolved jobs data, an isolved jobs api, to scrape isolved or ApplicantPro job postings for a job board or aggregator, isolved ATS listings, or change detection on isolved postings. Billed per job delivered with no start fee, and MCP-ready for Claude and other AI agents." +author: John Cole +author_url: https://github.com/johnisanerd +license: MIT +metadata: + version: "1.0" +--- + +# isolved Jobs, as Rows You Can Query + +Tenant slugs or job URLs in, live isolved jobs out as structured rows, each carrying the posting's own last-modified stamp so a scheduled run can return only what changed. + +## When to use this skill + +- Someone wants isolved jobs and does not want to read isolvedhire.com career sites by hand. +- You are filling a job board, a market-intel dashboard, or a sourcing tool with roles from isolved-hosted and ApplicantPro career pages. +- You need a real change feed: isolved publishes a per-job last-modified stamp, so a daily run can return only new or edited postings with no state to keep. +- You went looking for an isolved jobs API and found the vendor's product only, not a way to read the public postings as data. + +Not for: finding which employers run isolved in the first place. Use the companion `apify-companies-using-isolved` skill, built on the same Actor but shaped for tenant discovery. See `references/actor-index.md`. + +## What you get + +One dataset row per job. `resultType` separates `job` rows from `error` rows. + +Core fields: + +- `id`, `url` (canonical, safe as a dedupe key), `applyUrl`, `title` +- `organization`, `organizationUrl`, `tenant`, `tenantUrl` +- `employmentType`, `locationsDerived`, `isRemote` +- `datePosted`, `dateUpdated` (the sitemap last-modified stamp; date-granular) +- `salaryRaw` (the employer's published pay, verbatim; usually null on isolved) +- `descriptionMarkdown` (default add-on), `descriptionHtml`, `descriptionText` (opt-in add-ons) +- `source` (always `isolved`), `sourceType` (`ats`), `sourceUrl`, `scrapedAt` + +The Actor ships dataset views for the console: `overview`, `changes` (ordered by last-updated for monitoring), and `tenants`. + +## Prerequisites + +- Apify account (sign up at https://apify.com?fpr=9n7kx3&fp_sid=awesomeskills). +- Authentication via `apify login`, or an `APIFY_TOKEN` environment variable (Apify Console, Settings, Integrations). + +## The Actor + +- Store page: https://apify.com/johnvc/isolved-jobs-api?fpr=9n7kx3&fp_sid=awesomeskills +- Actor ID: `johnvc/isolved-jobs-api` +- Pricing: pay per event, no start fee. See the cost section below and `references/gotchas.md` for the live-price command. + +## Run it with the Apify CLI + +One tenant, full job rows, capped at 25: + +```bash +apify actors call "johnvc/isolved-jobs-api" -i '{"tenants":["isolved"],"maxJobs":25}' \ + --json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +Only postings that changed in the last day, the shape to put on a daily schedule: + +```bash +apify actors call "johnvc/isolved-jobs-api" -i '{"tenants":["isolved","davidsonoil"],"updatedAfter":"25h","maxJobs":200}' \ + --json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +Full-time roles only, descriptions off for cheaper metadata rows: + +```bash +apify actors call "johnvc/isolved-jobs-api" -i '{"tenants":["isolved"],"employmentType":["FULL_TIME"],"includeDescriptionMarkdown":false,"maxJobs":100}' \ + --json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +A legacy ApplicantPro site resolves automatically: + +```bash +apify actors call "johnvc/isolved-jobs-api" -i '{"tenants":["https://someco.applicantpro.com"],"maxJobs":25}' \ + --json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +Confirm the live schema and prices before a large batch: + +```bash +apify actors info "johnvc/isolved-jobs-api" --json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +Read the rows back from a finished run: + +```bash +apify datasets get-items --format json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +Every call carries the three flags this repo expects: `--json` (or `--format json`), `--user-agent apify-awesome-skills/apify-isolved-jobs-api`, and `2>/dev/null`. + +## Run it from Claude or another AI agent (MCP) + +The Actor is MCP-ready. Add the hosted server URL: + +`https://mcp.apify.com/?tools=actors,docs,johnvc/isolved-jobs-api` + +Then ask, for example: "Pull the newest isolved postings from these tenants in the last week and return title, location, and apply URL." MCP setup docs: https://docs.apify.com/platform/integrations/mcp + +## Workflow + +1. Start with one tenant and `maxJobs` around 25. Look at the row shape before you pay for a wide crawl. A tenant slug is the part before `.isolvedhire.com`. +2. Tenant slugs, full tenant URLs, and single job URLs all work as input, mixed freely. Legacy `applicantpro.com` URLs resolve too. +3. Filter at the source, not downstream. `titleKeywords`, `locationKeywords`, `employmentType`, `hasSalary`, and `updatedAfter` all drop jobs before they are billed. +4. Use `updatedAfter` for a change feed and `publishedAfter` for a genuinely-new-roles feed. `updatedAfter` is checked against the sitemap stamp before any page is fetched, so unchanged postings are never even downloaded. +5. Keep `includeDescriptionMarkdown` on for LLM pipelines; turn it off for metadata-only rows at a lower per-row cost. +6. Check `resultType` before treating a row as a job. An `error` row carries `errorCode` and a human-readable `errorMessage`. +7. Dedupe downstream on `url`. It is canonical and survives an employer editing the title. + +## Inputs + +- `tenants` (array): tenant slugs, tenant URLs, or single job URLs, mixed freely. Empty sweeps the bundled directory. +- `startUrls` (array): the same values in URL-list form; merged with `tenants`. +- `outputMode` (enum `jobs`, `urlsOnly`, `tenantsOnly`, default `jobs`) +- `titleKeywords`, `locationKeywords` (arrays): keep-only filters, run before billing. +- `employmentType` (array): schema.org codes like `FULL_TIME`, `PART_TIME`; plain wording is accepted. +- `hasSalary` (boolean, default false): keep only postings with a published salary. +- `updatedAfter` (string): `24h`, `7d`, `2w`, or an ISO date, against the sitemap last-modified stamp. +- `publishedAfter` (string): same grammar, against the posting's `datePosted`. +- `includeDescriptionMarkdown` (boolean, default true), `includeDescriptionHtml`, `includeDescriptionText` (default false) +- `report` (enum `none`, `markdown`, `html`): a whole-run digest in the key-value store. +- `maxJobs` (integer, default 100): the primary spend cap. `maxJobsPerTenant`, `maxTenants`, `maxConcurrency` refine it. +- `proxyConfiguration` (object): off by default; direct connections work. + +## Cost + +Billing is pay per event with no start fee, so a run that returns nothing costs almost nothing. Confirm live prices with the info command above rather than trusting a number copied here. + +The base `job-result` event fires once per delivered job row. Add-on events fire only on rows that carry the extra: `job-description-markdown` (on by default), `job-description-html`, `job-description-text`, and a flat `run-report`. Tenant discovery rows and URL index rows have their own cheaper events. + +Jobs removed by your filters are never charged. As a shape, 100 jobs with Markdown descriptions is small change. + +Suggested confirmation thresholds: mention the estimate under about $5, warn the user over about $5, get explicit confirmation over about $20. Present cost as "around $X", never as a guarantee. + +## Honest limits + +- **Change detection is date-granular.** The sitemap last-modified stamp is a date, so `updatedAfter` resolves to days, not hours. A `25h` window on a daily schedule is the right shape. +- Salary appears only when the employer publishes structured pay on the posting, which most isolved employers do not, so `salaryRaw` is often null. Nothing is inferred by a model. +- Public career-site data only. No applicant data, no recruiter contacts, nothing behind a login. +- US-focused. isolved career sites are almost entirely United States employers. + +## Troubleshooting + +- `board_not_found`: the tenant has no public job sitemap; it may not exist or has no live jobs. Check the slug or paste the tenant URL. +- `http_error`: the source answered abnormally; the row says which HTTP status. Retry once before assuming anything. +- `invalid_url`: the entry is not an isolved tenant slug, tenant URL, or job URL. +- Zero rows and no error row: your filters removed everything. Relax `updatedAfter` or `titleKeywords` first. +- Duplicates across runs: dedupe on `url`, never on title. + +See `references/gotchas.md` for cost guardrails and error recovery, and `references/actor-index.md` for the Actor routing table. + +## Related Actors + +- Greenhouse Job Board API: https://apify.com/johnvc/greenhouse-job-board-api?fpr=9n7kx3&fp_sid=awesomeskills +- Ashby Job Board API: https://apify.com/johnvc/ashby-job-board-scraper?fpr=9n7kx3&fp_sid=awesomeskills +- iCIMS Careers API: https://apify.com/johnvc/icims-careers-api?fpr=9n7kx3&fp_sid=awesomeskills +- Workday Careers API: https://apify.com/johnvc/workday-careers-api?fpr=9n7kx3&fp_sid=awesomeskills diff --git a/skills/apify-isolved-jobs-api/references/actor-index.md b/skills/apify-isolved-jobs-api/references/actor-index.md new file mode 100644 index 0000000..f58d009 --- /dev/null +++ b/skills/apify-isolved-jobs-api/references/actor-index.md @@ -0,0 +1,15 @@ +# Actor routing table + +One Actor powers both isolved skills; the skills differ in which output mode and question they are shaped for. + +| Question | Skill | Actor input shape | +|---|---|---| +| Job rows from named tenants | apify-isolved-jobs-api | default `jobs` mode, `tenants` list, filters | +| New or changed postings since yesterday | apify-isolved-jobs-api | `updatedAfter: "25h"` on a schedule | +| Which employers use isolved | apify-companies-using-isolved | `outputMode: "tenantsOnly"`, directory sweep | +| Does this employer have an isolved site | apify-companies-using-isolved | `outputMode: "tenantsOnly"`, your `tenants` list | +| Cheapest full index of a tenant | either | `outputMode: "urlsOnly"` | + +- Actor: https://apify.com/johnvc/isolved-jobs-api?fpr=9n7kx3&fp_sid=awesomeskills +- Actor ID: `johnvc/isolved-jobs-api` +- Related family: Greenhouse Job Board API (https://apify.com/johnvc/greenhouse-job-board-api?fpr=9n7kx3&fp_sid=awesomeskills) and iCIMS Careers API (https://apify.com/johnvc/icims-careers-api?fpr=9n7kx3&fp_sid=awesomeskills) share the row shape for cross-ATS merging. diff --git a/skills/apify-isolved-jobs-api/references/gotchas.md b/skills/apify-isolved-jobs-api/references/gotchas.md new file mode 100644 index 0000000..8eca5f4 --- /dev/null +++ b/skills/apify-isolved-jobs-api/references/gotchas.md @@ -0,0 +1,31 @@ +# Cost guardrails and error recovery + +## Live prices, not remembered ones + +```bash +apify actors info "johnvc/isolved-jobs-api" --json \ + --user-agent apify-awesome-skills/apify-isolved-jobs-api \ + 2>/dev/null +``` + +Read `pricingInfos` from the output. Events are per delivered row; filtered rows are never charged, and there is no start fee. + +## Guardrails + +- Cap every exploratory run: `maxJobs: 25` (jobs) or `maxTenants: 25` (discovery) until the row shape is confirmed. +- Filters run before billing. Push `titleKeywords`, `locationKeywords`, `employmentType`, `hasSalary`, and `updatedAfter` into the input rather than filtering downstream. +- Description formats are per-row add-on events. Metadata-only rows (all description toggles off) are the cheapest job rows. +- The whole-run `report` is one flat event; skip it in pipelines. +- `urlsOnly` mode is the cheapest full index of a tenant: links and change timestamps, no descriptions. + +## Error recovery + +- `board_not_found`: the tenant has no public job sitemap; it may not exist or has no live jobs. Paste the tenant URL when a slug will not resolve. +- `http_error`: transient upstream answer; the row says the HTTP status. Retry once, then check the tenant site in a browser. +- `invalid_url`: the entry is not an isolved tenant slug, tenant URL, or job URL. +- Errors are in-band dataset rows with `resultType: "error"`; pipelines should branch on `resultType`, not on run status. + +## Freshness facts worth knowing + +- isolved publishes a per-job last-modified stamp in the sitemap, so `updatedAfter` is a real new-or-changed cutoff, not just a new-postings one. It is date-granular, so use day-sized windows like `25h`. +- The bundled tenant directory is rebuilt from isolved's public sitemap index, which lists every tenant, so discovery re-enumerates the live universe rather than a frozen snapshot.