{
  "openapi": "3.0.1",
  "info": {
    "title": "Job Board Aggregator 🚀 - Indeed, LinkedIn & Glassdoor Scraper",
    "description": "Job board aggregator and multi-board scraper: LinkedIn, Indeed, Glassdoor, The Muse + 8 keyless boards in one run. ATS job scraper built in - watch Greenhouse, Lever and Ashby company boards. New-job-alerts mode returns only new postings. Cross-board dedup: one role bills once. CSV, JSON.",
    "version": "0.1",
    "x-build-id": "acfv3EhBHP0SX6trg"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/flash_scraper~multi-jobboard-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-flash_scraper-multi-jobboard-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/flash_scraper~multi-jobboard-scraper/runs": {
      "post": {
        "operationId": "runs-sync-flash_scraper-multi-jobboard-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/flash_scraper~multi-jobboard-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-flash_scraper-multi-jobboard-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "preset": {
            "title": "Preset",
            "enum": [
              "",
              "salary_research",
              "competitor_intel",
              "remote_only",
              "fresh_postings",
              "recruiter_leads"
            ],
            "type": "string",
            "description": "Pick a goal and the matching options below are switched on for you. A preset only fills options you left at their default — anything you set yourself always wins — and the run log plus <code>RUN_SUMMARY.preset_applied</code> list exactly what it changed.<br><br><b>Salary research</b> — keeps only rows that carry a salary, rewrites hourly and monthly pay into an annual figure, and sorts highest-paid first.<br><b>Competitor hiring intel</b> — turns on the company-details block; add the employers you are watching under <b>Only these companies</b> or the run stays unfiltered (the log warns you).<br><b>Remote-only sweep</b> — asks every board for remote roles only, which also brings in the four remote-only boards when you left the board list untouched. This is the one preset that can <b>raise</b> your bill, because those extra boards add rows.<br><b>Fresh postings only</b> — drops anything posted more than 7 days ago after scraping (dropped rows are never billed), and asks the boards for the same window too when that cannot clash with your job-type / easy-apply / remote choices.<br><b>Recruiter lead-gen</b> — company block on every row and staffing-agency reposts dropped.",
            "default": ""
          },
          "roleSelect": {
            "title": "Role (quick pick)",
            "enum": [
              "",
              "software engineer",
              "data analyst",
              "registered nurse",
              "sales representative",
              "product designer",
              "devops engineer",
              "machine learning engineer",
              "project manager",
              "accountant",
              "truck driver",
              "retail associate",
              "civil engineer",
              "lab technician",
              "customer support",
              "content writer",
              "security engineer",
              "financial analyst",
              "teacher",
              "chef",
              "electrician"
            ],
            "type": "string",
            "description": "Pick a common job title instead of typing one, e.g. <code>Nurse</code>. Leave it on <b>Custom</b> to use the <b>Search term</b> box below — and note the reverse: picking a role here <b>overrides</b> that box, and only <b>Search terms (multiple)</b> outranks it. This is not a fixed taxonomy: the choice is sent to the boards as plain keywords, exactly as if you had typed them.",
            "default": ""
          },
          "citySelect": {
            "title": "City (quick pick)",
            "enum": [
              "",
              "Remote",
              "New York, NY",
              "San Francisco, CA",
              "Los Angeles, CA",
              "Chicago, IL",
              "Austin, TX",
              "Seattle, WA",
              "Boston, MA",
              "Washington, DC",
              "Atlanta, GA",
              "Dallas, TX",
              "Denver, CO",
              "Miami, FL",
              "Phoenix, AZ",
              "Houston, TX",
              "Toronto, ON",
              "London",
              "Berlin",
              "Amsterdam",
              "Dublin",
              "Paris",
              "Sydney",
              "Bengaluru",
              "Singapore"
            ],
            "type": "string",
            "description": "Pick a metro instead of typing one, e.g. <code>Austin, TX</code>. Leave it on <b>Custom</b> to use the <b>Location</b> box below; picking a city here overrides that box, and only <b>Locations (multiple)</b> outranks it. Picking a non-US metro also switches <b>Indeed/Glassdoor country</b> to the matching country unless you set that field yourself — the run log and <code>RUN_SUMMARY</code> say so when it happens. <b>Remote</b> simply searches the word <code>Remote</code> as the location; turn on <b>Remote jobs only</b> below if you also want the strict filter and the remote-only boards.",
            "default": ""
          },
          "searchTerm": {
            "title": "Search term",
            "type": "string",
            "description": "Enter the job title or keywords sent to every selected board, e.g. <code>registered nurse</code>. Ignored when you pick a <b>Role (quick pick)</b> above or fill <b>Search terms (multiple)</b> below; boards treat this as free text, so quotes and boolean operators are not guaranteed to work.",
            "default": "software engineer"
          },
          "searchTerms": {
            "title": "Search terms (multiple)",
            "type": "array",
            "description": "Run up to 5 searches in one go, merged and de-duplicated so a job posting found by two terms is billed once. Overrides both <b>Search term</b> and <b>Role (quick pick)</b>; terms beyond the 5th are reported in the run status and in <code>RUN_SUMMARY.ignored_search_terms</code>, never dropped silently.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "location": {
            "title": "Location",
            "type": "string",
            "description": "Enter the city, state or country to search in, e.g. <code>New York, NY</code>. Ignored when you pick a <b>City (quick pick)</b> above or fill <b>Locations (multiple)</b> below; leave it empty to search anywhere, and remember that one country-wide value returns a single capped batch of job postings per board while three cities return three.",
            "default": "New York, NY"
          },
          "locations": {
            "title": "Locations (multiple)",
            "type": "array",
            "description": "Search several places in one run, e.g. <code>Austin, TX</code> then <code>Denver, CO</code> — this is the way past a board's per-query cap. Replaces <b>Location</b> and <b>City (quick pick)</b>; capped at 10 locations and 25 term × location searches, always reported in <code>RUN_SUMMARY</code>. <b>Cost warning:</b> every term × location pair asks each board for up to <b>Max job postings per board</b>, so 3 terms × 3 locations × 3 boards can bill up to 9× a single search — cross-search duplicates are still billed only once.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "sites": {
            "title": "Job boards",
            "type": "array",
            "description": "Choose which boards to scrape. LinkedIn, Indeed, Glassdoor and The Muse are the default (The Muse joined 2026-08-14 after measuring 12/12 success from the standard proxy pool; it carries no salary data). Seven more are remote-only public APIs (Remotive, Jobicy, Himalayas, Hacker News <i>Who is hiring?</i>, Remote OK, We Work Remotely, Working Nomads): they are left out of the default on purpose because they add nothing to a location search, and they auto-join only when <b>Remote jobs only</b> is on and this list is still exactly the default boards. DevITjobs US/UK are opt-in tech boards with salary on ~100% of rows (annual, currency as published). Glassdoor tops out near 28-30 postings per query - a board-side cap, not a bug - and intermittently 403s entire runs; when it does, the run status names it and the missing board bills nothing. The five boards marked <b>blocked at source</b> return nothing right now and cost nothing to leave selected.",
            "items": {
              "type": "string",
              "enum": [
                "linkedin",
                "indeed",
                "glassdoor",
                "muse",
                "remotive",
                "jobicy",
                "himalayas",
                "hn_hiring",
                "devitjobs_us",
                "devitjobs_uk",
                "remoteok",
                "weworkremotely",
                "working_nomads",
                "google",
                "zip_recruiter",
                "bayt",
                "bdjobs",
                "naukri"
              ],
              "enumTitles": [
                "LinkedIn",
                "Indeed",
                "Glassdoor",
                "The Muse",
                "Remotive · remote-only",
                "Jobicy · remote-only",
                "Himalayas · remote-only",
                "HN Who-is-Hiring · remote-only",
                "DevITjobs US · tech, ~100% salary",
                "DevITjobs UK · tech, ~100% salary",
                "Remote OK · remote-only",
                "We Work Remotely · remote-only",
                "Working Nomads · remote-only",
                "Google Jobs · blocked at source",
                "ZipRecruiter · blocked at source",
                "Bayt / MENA · blocked at source",
                "BDJobs / Bangladesh · blocked at source",
                "Naukri / India · blocked at source"
              ]
            },
            "default": [
              "linkedin",
              "indeed",
              "glassdoor",
              "muse"
            ]
          },
          "searchUrls": {
            "title": "Search URLs",
            "type": "array",
            "description": "Paste job-search URLs straight from your browser and skip the form, e.g. <code>https://www.indeed.com/jobs?q=python+developer&amp;l=Austin%2C+TX</code>. Each URL scrapes only the board it came from and, when set, this replaces <b>Search term</b>, <b>Locations</b> and <b>Job boards</b> above. Supported: LinkedIn (<code>linkedin.com/jobs/search</code>), Indeed (any country sub-domain) and Glassdoor (both URL forms); each URL's own filters — keywords, location, posted-within, remote, job type, experience level, sort, paging — are honored on that URL's job postings only, before de-duplication and before billing (the README lists the exact honored parameters per board). A URL we cannot parse is skipped and reported in <code>RUN_SUMMARY.search_url_errors</code> while the other URLs still run.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "atsCompanies": {
            "title": "Watch specific companies (ATS boards)",
            "type": "array",
            "description": "Company names — the actor reads each company's own careers board directly (Greenhouse, Lever or Ashby, all keyless, tried in that order) and merges the live openings into the same table as the job boards. Fresher than any aggregator, straight from the source. <b>Leave the search term untouched and you get each company's whole board</b>; type your own term and only matching roles are delivered (and billed). A company not on any of the three ATSes is named in the log and costs nothing. Up to 20 companies.",
            "items": {
              "type": "string"
            }
          },
          "maxResults": {
            "title": "Max job postings per board",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Set how many job postings to request from each board, e.g. <code>20</code> (1–500, values outside are clamped). You are billed per delivered row <i>after</i> de-duplication, so 3 boards × 30 bills up to 90 job postings minus duplicates; Glassdoor tops out near 28–30 per query whatever you set here.",
            "default": 20
          },
          "isRemote": {
            "title": "Remote jobs only",
            "type": "boolean",
            "description": "Ask each board for <b>its own</b> remote filter (LinkedIn <code>f_WT</code>, the Indeed and Glassdoor equivalents) instead of filtering rows here; off means on-site and hybrid job postings come back too. When this is ON and you left <b>Job boards</b> at its default, the four remote-only boards (Remotive, Jobicy, Himalayas, HN Who-is-Hiring) join the run automatically, which means <b>more job postings and a bigger bill</b>. Selecting exactly those same three boards does not opt out — the run cannot tell that apart from the default — so to keep a fixed set, pick any selection other than the default three (add or drop one). Indeed and Glassdoor honor their own remote filter; LinkedIn's guest search accepts <code>f_WT</code> and ignores it, returning the same on-site roles either way. So the Actor also drops rows whose own <code>is_remote</code> column says <code>false</code> after scraping, <b>before</b> billing — those rows are free. A job posting with no description carries no evidence either way and is kept, so read <code>is_remote</code> as \"nothing here says otherwise\" rather than a guarantee.",
            "default": false
          },
          "onlyNewJobs": {
            "title": "Only new jobs (monitoring mode)",
            "type": "boolean",
            "description": "For scheduled runs. Remembers every job this search has already delivered to you (in a private key-value store in <i>your</i> account) and delivers — and bills — only postings it has not seen before. Measured without it: two identical runs a day apart shared 47% of their rows, so a schedule was re-buying half its data every run. The first run with this on delivers everything and starts the memory; memory is kept per search (terms + locations + boards) for 90 days.",
            "default": false
          },
          "jobType": {
            "title": "Job type",
            "enum": [
              "",
              "fulltime",
              "parttime",
              "internship",
              "contract"
            ],
            "type": "string",
            "description": "Keep only one employment type, e.g. <b>Full-time</b>. Leave at <b>Any</b> to include every type.<br><br>Only Indeed and Glassdoor really filter this board-side: LinkedIn's guest search accepts the parameter and ignores it, and the five API boards have no employment-type filter at all. So the Actor also checks each job posting's own <code>job_type</code> after scraping and drops the ones that contradict your choice — <b>before</b> billing, so those rows are free. A job posting the board published with no type at all is kept, because nothing about it says it is the wrong type. Note that Indeed cannot combine this with <b>Posted within (hours)</b> and drops the job type for that board only, which the run log reports; the after-scraping check still runs.",
            "default": ""
          },
          "experienceLevel": {
            "title": "Experience level (best-effort)",
            "type": "array",
            "description": "Keep only job postings at these seniority levels, e.g. <b>Entry level</b>. Best-effort: when a board publishes the level we use it (100% of LinkedIn rows with <b>Full LinkedIn job details</b> on, plus Jobicy, Himalayas and The Muse), otherwise we read it off the job title (<code>Senior</code>/<code>Staff</code>/<code>Lead</code> → mid-senior, <code>Junior</code>/<code>Graduate</code> → entry, <code>Intern</code> → internship, <code>Director</code> → director, <code>VP</code>/<code>Chief</code> → executive). A job posting whose level we cannot determine is <b>kept</b>, never silently dropped, and filtered rows are never charged; leave empty to keep every level.",
            "items": {
              "type": "string",
              "enum": [
                "internship",
                "entry",
                "associate",
                "mid_senior",
                "director",
                "executive"
              ],
              "enumTitles": [
                "Internship",
                "Entry level",
                "Associate",
                "Mid-Senior level",
                "Director",
                "Executive"
              ]
            },
            "default": []
          },
          "requireSalary": {
            "title": "Only jobs with salary",
            "type": "boolean",
            "description": "Keep only job postings that carry a salary figure (<code>salary_min</code> or <code>salary_max</code>). Applied after de-duplication and before billing, so rows without a salary are never charged; Glassdoor and Indeed publish salary most often, LinkedIn rarely.",
            "default": false
          },
          "hoursOld": {
            "title": "Posted within (hours)",
            "minimum": 1,
            "type": "integer",
            "description": "Ask the boards for job postings published in the last N hours, e.g. <code>168</code> for the last week (<code>24</code> = last day). This is a board-side filter, so it cannot be combined with <b>Easy Apply only</b> on LinkedIn/Indeed or with job type / remote on Indeed — the clashing option is dropped for that board and the log says so. Leave empty for all ages, or use <b>Max posting age (days)</b> instead, which never clashes. Anything above 8760 (one year) behaves the same as leaving it empty — the age limit is dropped and your other board-side filters are all kept."
          },
          "maxAgeDays": {
            "title": "Max posting age (days)",
            "minimum": 1,
            "type": "integer",
            "description": "Drop job postings older than N days, e.g. <code>7</code> for the last week. Applied by the Actor after scraping and before billing, so filtered rows are never charged; unlike <b>Posted within (hours)</b> it works on every board and never clashes with another filter. Job postings the board published without a date are kept; empty keeps everything. Any number you type is applied literally — <code>400</code> means 400 days."
          },
          "targetCompanies": {
            "title": "Only these companies",
            "type": "array",
            "description": "Keep <b>only</b> job postings from these employers, e.g. <code>Deloitte</code> — everything else is dropped, which is how you watch who a competitor is hiring. Names are normalized the way de-duplication does them (<code>Google</code> also matches <code>Google LLC</code>, and <code>Amazon</code> keeps <code>Amazon Web Services</code> but not <code>Amazonia</code>); applied after de-duplication and before billing, so dropped rows are never charged. Leave empty to keep every company.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeCompanies": {
            "title": "Exclude companies",
            "type": "array",
            "description": "Drop these employers, e.g. a staffing agency or your own company. Case-insensitive and legal suffixes are ignored, so <code>Wipro</code> also excludes <code>Wipro Limited</code> and <code>Wipro Technologies</code> (same rule as <b>Only these employers</b>, and <code>Wiprox</code> is left alone); filtered job postings are never charged.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeTitleKeywords": {
            "title": "Exclude title keywords",
            "type": "array",
            "description": "Drop job postings whose title contains any of these words or phrases, e.g. <code>senior</code>, <code>intern</code>, <code>clearance</code>. Case-insensitive substring match against the title only (not the description), and filtered job postings are never charged.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeCities": {
            "title": "Exclude cities",
            "type": "array",
            "description": "Drop job postings whose location mentions any of these places, e.g. <code>San Francisco</code> or <code>Remote</code>. Whole-word, case-insensitive match against the location text exactly as the board published it — <code>Dallas</code> drops <code>Dallas, TX</code> while <code>Dall</code> drops nothing. Best-effort, because boards format locations inconsistently; filtered job postings are never charged.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeAgencies": {
            "title": "Exclude staffing agencies & recruiters",
            "type": "boolean",
            "description": "Drop job postings whose <b>company name</b> looks like a staffing agency, recruiter or body shop (staffing, recruit*, headhunt*, talent*, manpower, placement*, consultanc*, workforce, <code>HR solutions</code>…). Heuristic by design — it reads the company name only, so a neutrally-named re-poster slips through and a genuine employer called <code>Workforce</code> is dropped; use <b>Exclude companies</b> for exact control. Filtered job postings are never charged.",
            "default": false
          },
          "strictKeywordMatch": {
            "title": "Only job postings that mention my search term",
            "type": "boolean",
            "description": "Drop job postings whose title and description do not contain your search words. Off by default, because \"loosely related\" is what some searches want.<br><br>Turn it on for a narrow or unusual role. LinkedIn's guest search never answers \"nothing matched\" — it falls back to whatever it considers close, so a search for <code>underwater welder</code> in Fargo comes back full of plumbers and yard attendants, and you pay for them. This drops those rows before billing, so it can only ever <b>lower</b> your bill. Either way the run counts how many delivered job postings never mention your search term and reports it per board in <code>RUN_SUMMARY.rows_not_mentioning_search_term</code>, so you can check before deciding.",
            "default": false
          },
          "descriptionFormat": {
            "title": "Description format",
            "enum": [
              "markdown",
              "html"
            ],
            "type": "string",
            "description": "Choose the format of the <code>description</code> column. Markdown is the best input for LLMs and spreadsheets; HTML keeps the board's original markup.",
            "default": "markdown"
          },
          "descriptionHtml": {
            "title": "Raw HTML description column",
            "type": "boolean",
            "description": "Add a <code>description_html</code> column holding the description exactly as the board published it, next to the normal <code>description</code> in your chosen format — same run, no extra requests, no extra cost per job posting. Use the HTML for original formatting (links, lists, headings) and the Markdown for LLMs and spreadsheets; off by default because HTML roughly doubles dataset size.",
            "default": false
          },
          "includeCompanyDetails": {
            "title": "Company details block",
            "type": "boolean",
            "description": "Fill in the last of the company block — <code>company_ceo</code>, <code>company_banner</code>, <code>company_addresses_all</code> and the <code>company_details_filled</code> counter — three fields Indeed already returns and the scraping library throws away. Everything else in the block (<code>company_url</code>, industry, description, size band, revenue, addresses, logo, rating) ships either way: every row of every run carries all 50 columns, so this switch changes what is <b>filled in</b>, never which columns exist. It adds no requests and invents nothing — what a board does not publish stays null, and <code>company_details_filled</code> counts what each row actually got. Indeed supplies the richest block; LinkedIn adds industry and <code>company_url</code> when <b>Full LinkedIn job details</b> is on; Glassdoor adds rating and logo (it is the only board that publishes <code>company_rating</code>, so that column is null on roughly 70% of a mixed run); the remaining boards supply little or nothing — measured fill rates are in the README.",
            "default": false
          },
          "resumeKeywords": {
            "title": "Resume keywords (scoring only)",
            "type": "array",
            "description": "Score every job posting against your skills or phrases, e.g. <code>Python</code>, <code>Kubernetes</code>, <code>SOC 2</code>, <code>C++</code> — each row gains <code>matched_keywords</code> and <code>keyword_match_percent</code> (0–100). This <b>never</b> removes a job posting and never changes billing, it only annotates so you can sort or triage by fit; case-insensitive substring match, so <code>c++</code>, <code>.NET</code> and <code>node.js</code> work as written. Pairs best with <b>Sort order</b> = Relevance.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "sortBy": {
            "title": "Sort order",
            "enum": [
              "",
              "relevance",
              "date_desc",
              "salary_desc"
            ],
            "type": "string",
            "description": "Choose the order of the final, de-duplicated job postings, e.g. <b>Newest first</b>. Applied before the charge cap, so a truncated run keeps the job postings you ranked highest. <b>Relevance</b> means best resume-keyword match first, then job postings found on the most boards, then newest; rows missing the chosen key (no date, no salary) always sort last, and <b>Board order</b> keeps the original behaviour.",
            "default": ""
          },
          "enforceAnnualSalary": {
            "title": "Annual salary normalization",
            "type": "boolean",
            "description": "Convert hourly and monthly salaries into an annual figure in <code>salary_min</code>/<code>salary_max</code> where the board gave enough to do it. You rarely need this: <code>salary_min_annual</code> and <code>salary_max_annual</code> are computed on every row regardless, and this switch is what rewrites the raw columns too.",
            "default": false
          },
          "countryIndeed": {
            "title": "Indeed/Glassdoor country",
            "type": "string",
            "description": "Choose which country's Indeed and Glassdoor site to query, e.g. <code>usa</code>, <code>uk</code>, <code>canada</code>, <code>india</code>, <code>germany</code>. Only these two boards use it — LinkedIn and the API boards read the country out of your location text.<br><br>You can usually leave this alone: if you type a location that names a country (<code>Berlin, Germany</code>, <code>Casablanca, Morocco</code>) the Actor points Indeed at that country's site for you, exactly as picking a non-US metro under <b>City (quick pick)</b> does — anything you type here always wins. A spelling this Actor does not recognise is reported in the log and falls back to <code>usa</code> instead of failing the run.",
            "default": "usa"
          },
          "linkedinFetchDescription": {
            "title": "Full LinkedIn job details",
            "type": "boolean",
            "description": "Open each LinkedIn job posting's own page — on by default, because it lifts description 64%→100%, job_type 24%→65% and company_industry 2%→38% (measured on 72 rows) at the same price per row. With this OFF, LinkedIn rows carry no <code>description</code>, <code>job_type</code>, <code>job_level</code>, <code>job_function</code> or <code>company_industry</code> at all — but the run is faster by one request per LinkedIn job posting.",
            "default": true
          },
          "linkedinCompanyIds": {
            "title": "LinkedIn company IDs",
            "type": "array",
            "description": "Restrict LinkedIn job postings to these numeric company IDs, e.g. <code>1441</code> — take them from the <code>f_C</code> parameter of a LinkedIn search URL. LinkedIn only; every other board ignores it.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "distance": {
            "title": "Distance (miles)",
            "minimum": 0,
            "type": "integer",
            "description": "Set the search radius around the location in miles, e.g. <code>25</code>. LinkedIn and Indeed honor it; Glassdoor and the API boards ignore it, and it does nothing at all without a location."
          },
          "easyApply": {
            "title": "Easy Apply only",
            "type": "boolean",
            "description": "Keep only direct/easy-apply job postings on LinkedIn and Indeed; the other boards ignore it. Cannot be combined with <b>Posted within (hours)</b> on those two boards — when both are set, this one is dropped for them and the log says so.",
            "default": false
          },
          "offset": {
            "title": "Offset",
            "minimum": 0,
            "type": "integer",
            "description": "Skip the first N job postings on each board, e.g. <code>50</code> to page past a run you already have. Combine with <b>Max job postings per board</b> to walk a large search in chunks. LinkedIn, Indeed and Glassdoor page server-side; the five API boards have no paging of their own, so the Actor fetches N extra job postings there and hands you the tail. Either way boards apply it to their own ordering, so it is not a stable cursor — a posting added between two runs can shift the window."
          },
          "googleSearchTerm": {
            "title": "Google Jobs query",
            "type": "string",
            "description": "Enter a full Google Jobs search string, e.g. <code>software engineer jobs near New York since yesterday</code>. Leave empty and one is built from your search term plus location — and note Google Jobs is blocked at the source right now, so this changes nothing today."
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Datacenter proxy (the default) is what you want: measured 2026-08-11 on the same search, it returned 52 rows in 50s for $0.003, against 54 rows in 67s for $0.015 on residential — 96% of the rows at a quarter of the price. Residential is billed per gigabyte; datacenter is not. Switch to RESIDENTIAL only if Glassdoor starts coming back short for your search. Running with no proxy at all is the worst of the three: Indeed and Glassdoor time out, so you get LinkedIn only (20 rows) and still pay more, because the failing requests burn compute.",
            "default": {
              "useApifyProxy": true
            }
          },
          "userAgent": {
            "title": "User agent",
            "type": "string",
            "description": "Send a custom <code>User-Agent</code> header instead of the built-in one, e.g. a current desktop Chrome string. Leave empty unless a board is blocking the default."
          },
          "caCert": {
            "title": "CA certificate",
            "type": "string",
            "description": "Point to a custom CA certificate, by path or by contents — only needed when your own proxy terminates TLS with a private root. Leave empty for Apify Proxy."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}