{
  "openapi": "3.0.1",
  "info": {
    "title": "Jobs.ie Scraper - Ireland Job Listings, Salary & Employer",
    "description": "Scrape jobs.ie, Ireland's national job board: title, employer, county, salary, work type, industry, posted & expiry dates, GPS and the full description. Filter by direct employer vs agency, contract type, salary and posted-within. Daily incremental mode.",
    "version": "0.1",
    "x-build-id": "7aqtFVgQ9jP3W1yiC"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~jobs-ie-jobs-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-jobs-ie-jobs-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/scrapersdelight~jobs-ie-jobs-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-jobs-ie-jobs-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/scrapersdelight~jobs-ie-jobs-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-jobs-ie-jobs-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": {
          "keywords": {
            "title": "🔍 Keywords",
            "type": "string",
            "description": "Job title, skill or town, exactly as you would type it into jobs.ie — e.g. 'care assistant', 'python developer', 'dublin', 'hgv driver'. Multi-word terms work ('care assistant' becomes /jobs/care-assistant). Leave empty to sweep every live job in Ireland."
          },
          "location": {
            "title": "📍 Location",
            "type": "string",
            "description": "Town, city, county, province or region — e.g. 'dublin', 'cork', 'county-kildare', 'galway', 'leinster', 'munster', 'connacht', 'ulster', 'northern-ireland'. jobs.ie always applies a +20 km radius to a location (its own heading says so), so there is no separate radius setting. Leave empty to search the whole country. NOTE: a location and a keyword are two different corpora on jobs.ie. Location 'dublin' builds /jobs/in-dublin — 1,618 matches on 2026-09-06 (run zUY8UfxGvRlh9hwff), 'jobs in County Dublin + 20 km'. Typing dublin as the KEYWORD builds /jobs/dublin — 1,129 that same hour (run MSkDGZRjDeoxG4LHk), jobs whose text mentions Dublin. The filter breakdowns in the README are measured on the keyword path."
          },
          "searchTerms": {
            "title": "➕ Extra searches (one per line)",
            "type": "array",
            "description": "Run several searches in ONE run. Each line is either a keyword ('chef') — which reuses the Location above — or 'keyword in location' ('care assistant in cork'). jobs.ie carried 3,385 live postings nationally on 2026-09-06, so stacking searches is how you fill a dataset. Results are deduplicated on the jobs.ie job id across every search.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "🔗 Start URLs (advanced)",
            "type": "array",
            "description": "Paste jobs.ie URLs directly and skip the builder above. A search URL (https://www.jobs.ie/jobs/chef/in-cork?postedWithin=7) is paginated; a single posting URL (https://www.jobs.ie/job/chef/compass-group-job107812345) is fetched on its own with full detail. Anything you paste here overrides Keywords/Location.",
            "default": [],
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "contractType": {
            "title": "📄 Contract type",
            "enum": [
              "any",
              "permanent",
              "part-time",
              "contract",
              "temporary",
              "work-from-home"
            ],
            "type": "string",
            "description": "Server-side filter, applied as a jobs.ie path prefix. MEASURED 2026-09-06 on /jobs/dublin (1,125 matches): permanent 931 · part-time 119 · contract 54 · temporary 84 · work-from-home 107. These overlap - a role can be both permanent and work-from-home.",
            "default": "any"
          },
          "postedWithin": {
            "title": "🗓️ Posted within",
            "enum": [
              "any",
              "1",
              "3",
              "7",
              "14"
            ],
            "type": "string",
            "description": "Only jobs posted in the last N days. MEASURED 2026-09-06 on /jobs/dublin (1,125 matches): 1 day 95 · 3 days 458 · 7 days 841 · 14 days 1,105. This is the lever for a recurring feed — pair it with Incremental mode.",
            "default": "any"
          },
          "minSalary": {
            "title": "💶 Minimum salary (EUR)",
            "minimum": 0,
            "type": "integer",
            "description": "Only jobs paying at least this much, in the period chosen below. 0 = no salary filter. MEASURED 2026-09-06 on /jobs/dublin (1,125 matches): €40,000/year 423 · €80,000/year 73 · €15/hour 740 · €50/hour 41. Note that many Irish postings say '€ Not Disclosed' — those are excluded by jobs.ie when a salary filter is on.",
            "default": 0
          },
          "salaryPeriod": {
            "title": "💶 Salary period",
            "enum": [
              "year",
              "day",
              "hour"
            ],
            "type": "string",
            "description": "How to read the minimum salary above — per year, per day (contractor rates) or per hour.",
            "default": "year"
          },
          "companyType": {
            "title": "🏢 Company type",
            "enum": [
              "any",
              "direct-employer",
              "agency"
            ],
            "type": "string",
            "description": "THE lead-gen lever. 'Direct employer' = companies hiring for themselves — your cold-call roster. 'Agency' = recruitment agencies — competitor intel, or your prospect list if you sell TO recruiters. MEASURED 2026-09-06 on /jobs/dublin (1,125): direct employers 649 · agencies 476.",
            "default": "any"
          },
          "companyId": {
            "title": "🏢 Company ID (one employer's whole board)",
            "minimum": 0,
            "type": "integer",
            "description": "Scrape every live posting from ONE employer. The id is the `cmpId=` number in any jobs.ie company link, and it is returned on every row as `company_id` — so you can discover it with one broad run, then monitor that employer. 0 = off.",
            "default": 0
          },
          "maxItems": {
            "title": "💯 Number of jobs",
            "minimum": 0,
            "type": "integer",
            "description": "Stop after this many jobs — this is also the hard cap on what you are billed. 0 = every job jobs.ie serves for your query (the whole national board was 3,385 live postings on 2026-09-06). The cap counts rows you will actually RECEIVE: every filter — cross-run dedupe, incremental, keywords, promoted, reposts AND the posted-date filter, including the exact date read from the detail page — is applied BEFORE a row counts toward the cap, and the crawl keeps paging until the cap is full or jobs.ie runs out. Proof, both on 2026-09-06 over the same 45-match board: capped run 80fgRz66cIo7x0x2e (maxItems 25, details on, postedAfter 2026-08-25) returned 24, and the uncapped control m3aJ4j6dCx9rYcNF9 on the identical filter returned the same 24 — the board held no 25th matching job. When a run does come in under the cap the log says which of the three reasons it was: your filters plus an exhausted board, a truncated crawl (maxPages), or your Apify max-total-charge cap.",
            "default": 100
          },
          "maxPages": {
            "title": "📚 Max search pages",
            "minimum": 0,
            "type": "integer",
            "description": "Belt-and-braces cap on how many 25-job result pages to walk per search. 0 = no page cap (the job count above still applies).",
            "default": 0
          },
          "includeRelatedJobs": {
            "title": "Include out-of-scope \"similar\" jobs",
            "type": "boolean",
            "description": "OFF by default, and you almost certainly want it off. When a search matches fewer than 25 jobs, jobs.ie pads the same results list with unrelated 'similar' jobs and gives no visual break — MEASURED 2026-09-06: /jobs/nurse/in-galway says '1 Nurse job in County Galway + 20 km' and renders 12 cards, 11 of them out of scope. This scraper reads the real match count from the page heading and drops the padding, so you are never billed for it. Turn this on only if you want that padding too; those rows are flagged `is_related_match: true`.",
            "default": false
          },
          "includeDetails": {
            "title": "📄 Fetch full job details",
            "type": "boolean",
            "description": "Opens each posting for the exact posted date, expiry date, employment type, industry taxonomy, work type (Permanent/Temporary/Contract — this is the ONLY place it exists), full address, GPS coordinates and the complete job description. Costs one extra request per job and is billed as its own cheaper event, so leave it off when you only need the lead list. With it OFF these 14 of the 37 columns are null on every row (measured 2026-09-06, run FrlXsVHESixWU0Dm2): town, region, postcode, country, latitude, longitude, work_type, employment_type, industry, direct_apply, date_posted, valid_through, description_text, description_html.",
            "default": false
          },
          "descriptionFormat": {
            "title": "Description format",
            "enum": [
              "text",
              "html",
              "both",
              "none"
            ],
            "type": "string",
            "description": "How to return the full description when details are fetched. 'None' keeps the row small when you only want the structured fields.",
            "default": "text"
          },
          "descriptionMaxLength": {
            "title": "Description max length",
            "minimum": 0,
            "type": "integer",
            "description": "Truncate the description to this many characters, ellipsis included - set 200 and you get exactly 200. 0 = keep the whole thing (measured 2026-09-06 over 30 postings: 1,304-13,309 characters, median 6,263).",
            "default": 0
          },
          "postedAfter": {
            "title": "Posted on or after",
            "type": "string",
            "description": "Client-side date filter, e.g. 2026-08-01. Exact when 'Fetch full job details' is on (it compares the posting's real datePosted); with details off it falls back to the card's relative text ('3 weeks ago') and is approximate — use 'Posted within' above for a precise server-side cut. With details ON the exact test runs INSIDE the crawl, page by page, so it never decides on a truncated sample and the cap still fills; the cost is one detail fetch per candidate card, including cards the date test then drops. Dropped rows are never billed (run UNly7pVXDQJ0LTgCV, 2026-09-06: 9 detail pages fetched, 9 dropped on the exact date, 0 rows charged)."
          },
          "postedBefore": {
            "title": "Posted on or before",
            "type": "string",
            "description": "Client-side date filter, e.g. 2026-08-31. Same accuracy, same in-crawl ordering and the same billing note as 'Posted on or after' above."
          },
          "includeKeywords": {
            "title": "Must contain (any of)",
            "type": "array",
            "description": "Keep only jobs whose title, company or snippet contains at least one of these words. Case-insensitive. On a single-posting start URL there is no card to read, so the test is re-run after that posting's detail page is fetched, over its title, company and full description — and a row that fails it is never delivered and never billed (run Qf9U8YUqYKafcExXM, 2026-09-06: 0 rows, 0 charged).",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeKeywords": {
            "title": "Must NOT contain",
            "type": "array",
            "description": "Drop jobs whose title, company or snippet contains any of these words — e.g. 'apprentice', 'unpaid', 'internship'. Case-insensitive. On a single-posting start URL there is no card to read, so the test is re-run after that posting's detail page is fetched, over its title, company and full description — and a row that fails it is never delivered and never billed (run a08URQfSoptruM4JZ, 2026-09-06: 0 rows, 0 charged).",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeSponsored": {
            "title": "Exclude promoted listings",
            "type": "boolean",
            "description": "Drop paid placements, using jobs.ie's own flags for the card (sponsored, top job, highlighted, partnership, or a FEATURED / PREMIUM / TOP JOB badge) rather than the visible label. Honest measurement, 2026-09-06: jobs.ie currently runs NO promoted inventory - 0 of 1,126 cards on a complete /jobs/dublin sweep were flagged - so this normally drops nothing. Turning it on makes every search page parse jobs.ie's hydration blob, which roughly doubles the run's peak memory. The 'NEW' badge means recently posted, not promoted, and is never dropped.",
            "default": false
          },
          "skipReposts": {
            "title": "Collapse reposts",
            "type": "boolean",
            "description": "Keep only the first posting for each identical job title + company pair. Irish agencies repost the same role under new ids; this collapses them so you do not pay twice for one opening. A collapsed repost still counts as a page's 'new id' for end-of-results detection, so a page full of reposts can never end the crawl early (run zHeEhTKBV8YtM8RI3, 2026-09-06: page 2 read 25 cards, kept 23, and the seen-id count still advanced by the full 25).",
            "default": false
          },
          "dedupeAcrossRuns": {
            "title": "♻️ Never return the same job twice (across runs)",
            "type": "boolean",
            "description": "Remembers every job id already delivered and drops it in all future runs, forever. Ideal for topping up a CRM. State lives in a named key-value store, so it survives between runs.",
            "default": false
          },
          "dedupeStoreName": {
            "title": "Dedupe store name",
            "type": "string",
            "description": "Name of the key-value store holding the 'already delivered' job ids. Use different names to keep separate campaigns apart.",
            "default": "jobs-ie-seen"
          },
          "incrementalMode": {
            "title": "🆕 Incremental mode — only what is new since the last run",
            "type": "boolean",
            "description": "Compares this run against the ids this state key has already seen and returns only job ids that were not there before. The first run seeds the baseline and returns everything; every later run ADDS what it saw to the baseline (a capped run only walks part of the board, so replacing the baseline with one run's ids would re-deliver everything it did not reach). This plus 'Posted within = 24 hours' plus a daily schedule is the new-jobs monitor: jobs.ie postings expire exactly 30 days after they are posted, so the value here is the flow, not the stock.",
            "default": false
          },
          "stateKey": {
            "title": "Incremental state key",
            "type": "string",
            "description": "Name this monitor. Different searches on a schedule should each use their own key so their baselines do not collide.",
            "default": "default"
          },
          "sortBy": {
            "title": "Sort results",
            "enum": [
              "relevance",
              "date-desc",
              "salary-desc"
            ],
            "type": "string",
            "description": "CLIENT-SIDE only, and deliberately honest about it: jobs.ie has NO server-side sort parameter — ?sortby=2 and ?sort=date were measured returning the same match count AND the same first job id as the unsorted page. So this reorders the rows this run collected; it does not change which rows jobs.ie hands over. To bias which jobs you get toward recent ones, use 'Posted within', which is a real server-side filter.",
            "default": "relevance"
          },
          "outputFields": {
            "title": "Output columns",
            "type": "array",
            "description": "Keep only these columns, in this order — e.g. company, title, location, salary_raw, job_url. Leave empty for all 37 fields. 14 columns (town, region, postcode, country, latitude, longitude, work_type, employment_type, industry, direct_apply, date_posted, valid_through, description_text, description_html) are null unless 'Fetch full job details' is on, so asking for only those with the add-on off gives you rows of nulls — and, with 'Drop empty fields' also on, no rows at all (see that setting).",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeEmptyFields": {
            "title": "Drop empty fields",
            "type": "boolean",
            "description": "Omit null/empty keys from each row. Makes JSON output smaller; CSV exports stay aligned either way. This runs AFTER 'Output columns' has narrowed the row, so it can strip a row down to nothing: any row left with no fields at all is dropped before delivery and is never charged (measured: outputFields [date_posted, work_type, industry] + this switch, with the detail add-on off, delivered 6 empty {} rows and charged 6 events on build 0.1.13 — the same input now delivers 0 rows and charges $0). The run log names the column that emptied them.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "jobs.ie is unusable from Apify datacenter IPs (re-measured 2026-09-06 on /jobs/dublin: datacenter 0 of 3 - one 403 and two 45-second read timeouts - while Irish RESIDENTIAL was 3 of 3, 200 + 25 cards in 2.0-5.2s), so this defaults to an Irish RESIDENTIAL proxy. GB and US residential also work if you need them. Leave it as-is unless you know what you are doing.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ],
              "apifyProxyCountry": "IE"
            }
          },
          "requestConcurrency": {
            "title": "Request concurrency",
            "minimum": 1,
            "maximum": 8,
            "type": "integer",
            "description": "Parallel requests for the detail add-on. jobs.ie answers in about 2 seconds; keep this modest to stay polite and avoid resets.",
            "default": 3
          },
          "maxRequestRetries": {
            "title": "Max retries per request",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Each retry uses a brand-new proxy session. jobs.ie occasionally stalls a request against a ~2.5s median (measured 2026-09-06: 46 consecutive search pages, 0 stalls, median 2.49s, slowest 5.99s; detail pages did throw two proxy UPSTREAM504s that retries absorbed), so do not lower this.",
            "default": 5
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}