{
  "openapi": "3.0.1",
  "info": {
    "title": "s1jobs Scraper – Scotland & UK Jobs, Salaries & Details",
    "description": "Scrape jobs from s1jobs.com across Scotland and the UK. Search by keyword, location, or URLs. Returns title, parsed salary band, company, logo, GPS location, skills, contract type, and 90+ fields per job, with optional full description, postcode, and apply links.",
    "version": "1.0",
    "x-build-id": "yQWTmp8Wi3oCajQda"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~s1jobs-com-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-s1jobs-com-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/abotapi~s1jobs-com-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-s1jobs-com-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/abotapi~s1jobs-com-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-s1jobs-com-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",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "search",
              "url"
            ],
            "type": "string",
            "description": "How to find jobs: build a search from filters, or paginate URLs you paste.",
            "default": "search"
          },
          "locations": {
            "title": "Locations",
            "type": "array",
            "description": "Towns, cities, or counties. Examples: 'Glasgow', 'Edinburgh', 'Aberdeen', 'Fife'. One search runs per location. s1jobs searches the area around the place you name, so nearby commutable towns come back too (a Glasgow search also returns jobs in Renfrew and Wishaw). If s1jobs does not list the name on its own, the closest name it does list is used instead, for example 'Oban' becomes 'Oban, Argyll and Bute'. Leave empty to list jobs from everywhere. Search mode only: this builds the search URL and is NOT applied to the URLs you paste in URL mode.",
            "items": {
              "type": "string"
            }
          },
          "keywords": {
            "title": "Keywords",
            "type": "string",
            "description": "Role, skill, or company to search for. Example: 'nurse', 'warehouse', 'project manager', 'driver'. Leave empty to list all jobs in the location. Search mode only: this builds the search URL and is NOT applied to the URLs you paste in URL mode."
          },
          "contractType": {
            "title": "Contract type",
            "enum": [
              "any",
              "permanent",
              "contract",
              "temporary"
            ],
            "type": "string",
            "description": "Filter by contract arrangement. Search mode only: this filter is applied to search results and is NOT applied to the URLs you paste in URL mode.",
            "default": "any"
          },
          "workingHours": {
            "title": "Hours",
            "enum": [
              "any",
              "full",
              "part"
            ],
            "type": "string",
            "description": "Filter by full time or part time. Search mode only: this filter is applied to search results and is NOT applied to the URLs you paste in URL mode.",
            "default": "any"
          },
          "advertiserType": {
            "title": "Advertiser type",
            "enum": [
              "any",
              "employer",
              "agency"
            ],
            "type": "string",
            "description": "Restrict to direct employers or recruitment agencies. Search mode only: this filter is applied to search results and is NOT applied to the URLs you paste in URL mode.",
            "default": "any"
          },
          "minSalary": {
            "title": "Minimum salary",
            "minimum": 0,
            "type": "integer",
            "description": "Lowest salary to include. Combined with the salary period below. Jobs that quote no numeric salary (for example 'Competitive') are always kept. Leave empty for no minimum. Search mode only: this filter is applied to search results and is NOT applied to the URLs you paste in URL mode."
          },
          "salaryType": {
            "title": "Salary period",
            "enum": [
              "annual",
              "daily",
              "hourly"
            ],
            "type": "string",
            "description": "Unit for the minimum salary filter. Search mode only, exactly like the minimum salary it belongs to.",
            "default": "annual"
          },
          "publicSectorOnly": {
            "title": "Public sector only",
            "type": "boolean",
            "description": "Keep only public-sector roles. Search mode only: this filter is applied to search results and is NOT applied to the URLs you paste in URL mode.",
            "default": false
          },
          "hasVideoOnly": {
            "title": "Jobs with video only",
            "type": "boolean",
            "description": "Keep only jobs that include a recruiter video. Search mode only: this filter is applied to search results and is NOT applied to the URLs you paste in URL mode.",
            "default": false
          },
          "postedWithinHours": {
            "title": "Posted within (hours)",
            "minimum": 1,
            "type": "integer",
            "description": "Keep only jobs published within the last N hours (for example 24 for the last day). Leave empty for no limit. Search mode only: it is NOT applied to the URLs you paste in URL mode. Combine with Sort by Most recent to stop early on fresh jobs."
          },
          "sortBy": {
            "title": "Sort by",
            "enum": [
              "relevance",
              "date"
            ],
            "type": "string",
            "description": "Result ordering. Search mode only: it is written into the search URL. In URL mode the ordering already present in the URL you paste is used instead.",
            "default": "relevance"
          },
          "urls": {
            "title": "Search URLs",
            "type": "array",
            "description": "s1jobs.com pages to scrape (URL mode). Supported: search-result and listing pages (keyword, location, category, category plus location, collection, filtered, sorted or paginated), employer pages (an employer's jobs page or company profile, which return that employer's live jobs), and single job pages. A page number in a listing URL is honoured as the starting point. Company-directory index pages and links to other sites are skipped with a warning. The field is pre-filled with one example per supported page type. Single job pages are served first, then the listing and employer pages in the order given; the Total jobs limit is shared across all of them and each URL gets an equal share of it, so with the default limit the last samples can return few or no rows. Raise the limit or remove URLs you do not need. The Search mode fields above (locations, keywords, contract type, hours, advertiser type, minimum salary, public sector only, jobs with video only and sort) are NOT applied to these URLs, so build the filtering you want into the URL itself.",
            "default": [
              "https://www.s1jobs.com/job/registered-nurse-127537056",
              "https://www.s1jobs.com/jobs/highland-home-carers-3050125",
              "https://www.s1jobs.com/companies/newcross-healthcare-solutions-3098551",
              "https://www.s1jobs.com/jobs/apprenticeship-and-graduate/",
              "https://www.s1jobs.com/jobs/accountancy/glasgow/",
              "https://www.s1jobs.com/jobs/?Keywords=nurse&JobLocation.City=glasgow",
              "https://www.s1jobs.com/jobs/?Keywords=nurse&JobLocation.City=glasgow&page=2",
              "https://www.s1jobs.com/jobs/?JobLocation.City=Aberdeen&ContractType=permanent&WorkingHours=full&Sort=date",
              "https://www.s1jobs.com/jobs/",
              "https://www.s1jobs.com/jobs/glasgow/?Keywords=nurse",
              "https://www.s1jobs.com/jobs/aberdeen/",
              "https://www.s1jobs.com/jobs/accountancy/",
              "https://www.s1jobs.com/jobs/accountancy/?page=2",
              "https://s1jobs.com/jobs/?Keywords=nurse"
            ],
            "items": {
              "type": "string"
            }
          },
          "fetchDetails": {
            "title": "Fetch full job details",
            "type": "boolean",
            "description": "Fetch the per-job record to add the full description, postcode, structured location, salary band, matched and unmatched skills, accreditations and apply links. Adds ~20 fields. On = richest output (recommended). Off = fast, listing fields only.",
            "default": true
          },
          "maxListings": {
            "title": "Max jobs",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "Total jobs to collect across all searches. This is THE limit on the run (default 20). Set 0 for no limit (a search still stops after 1000 pages). Maximum 100000.",
            "default": 20
          },
          "maxPages": {
            "title": "Max pages per search",
            "minimum": 0,
            "type": "integer",
            "description": "Optional per-search safety stop on pages walked (20 jobs per page). This does NOT cap the run - leave it empty (unlimited) and the run defers to Max jobs for its size, walking every result page. Only set a value if you want to hard-stop pagination for one location/URL early. A hard ceiling of 1000 pages per search always applies."
          },
          "proxy": {
            "title": "Proxy",
            "type": "object",
            "description": "Connection settings. The default connection works well for this site. You can pick a different connection here for very large runs."
          },
          "maxResidentialRequests": {
            "title": "Residential request cap",
            "minimum": 0,
            "type": "integer",
            "description": "Safety cap on how many Residential proxy requests this run may use. 0 = unlimited. This cap stops a run from overspending on Residential bandwidth.",
            "default": 0
          },
          "maxResidentialMB": {
            "title": "Residential traffic budget (MB)",
            "minimum": 0,
            "type": "integer",
            "description": "Safety cap on how many megabytes of Residential proxy traffic this run may transfer. 0 = unlimited. Once spent, the run keeps going on the cheaper default connection so it can never silently overspend on Residential bandwidth.",
            "default": 0
          },
          "resumeFromRunId": {
            "title": "Resume from a previous run (optional)",
            "type": "string",
            "description": "ID of a previous run of this actor (or a dataset ID). Jobs already in that dataset (matched by job id) are skipped, so this run returns only NEW jobs, a delta. Combine both runs' datasets for the full set. Leave empty for a normal fresh run. For recurring daily monitoring of the same search, use Incremental mode below instead."
          },
          "incrementalMode": {
            "title": "Incremental changes for scheduled runs",
            "type": "boolean",
            "description": "Turn this on for daily or recurring monitoring. The first run returns all matching jobs as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED jobs. Turn on \"Emit unchanged\" or \"Emit expired\" only when you also want those jobs returned (and billed). State is kept separately for each search/URL and detail-mode setup; use State key when you want to name or deliberately share a monitoring campaign. A job counts as UPDATED when any returned field changes (title, description, salary, contract, location, dates, flags and so on); scrapedAt, sourceSearchUrl and any field listed in Ignore fields never do. A job whose detail fetch fails in a run is skipped until the next run. To continue one specific interrupted run instead, use Resume from a previous run above.",
            "default": false
          },
          "stateKey": {
            "title": "State key (optional, incremental mode only)",
            "type": "string",
            "description": "Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/URL and detail settings: different searches then never mix state with each other."
          },
          "emitUnchanged": {
            "title": "Emit unchanged jobs (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return jobs that have not changed since the last run, marked UNCHANGED. This returns (and bills) extra rows you already have, so leave it off unless you specifically want the full snapshot every run.",
            "default": false
          },
          "emitExpired": {
            "title": "Emit expired jobs (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return jobs that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, not when Max jobs capped it or when Resume was used. This returns (and bills) extra synthetic rows, so leave it off unless you need expiry tracking.",
            "default": false
          },
          "ignoreFieldsForChanges": {
            "title": "Ignore fields when detecting changes (incremental mode only)",
            "type": "array",
            "description": "Optional. Field names that never make a job UPDATED, in addition to the built-in handling (scrapedAt and sourceSearchUrl are already ignored). They are still returned in every row. Use a dotted name such as location.text to ignore one key inside an object. Example: skills, or advertType if you do not care about those changes.",
            "items": {
              "type": "string"
            }
          },
          "mcpConnectors": {
            "title": "Pipe results into your apps (optional)",
            "type": "array",
            "description": "Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON - the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com)."
          },
          "notionParentPageUrl": {
            "title": "Notion parent page (Notion connector only)",
            "type": "string",
            "description": "URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors."
          },
          "maxNotifyListings": {
            "title": "Max items to export per connector",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Cap on items written to each connector per run. Does not affect the dataset.",
            "default": 50
          }
        }
      },
      "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}