{
  "openapi": "3.0.1",
  "info": {
    "title": "CWJobs UK Scraper - Tech Jobs, Salaries & Employers",
    "description": "Scrape CWJobs.co.uk tech and IT job listings by keyword, location, salary, work type, date or URL. Extract job titles, salary ranges, GPS coordinates, full descriptions, posting dates and employer details. Supports advanced filters, sorting and structured data for job market research.",
    "version": "1.1",
    "x-build-id": "Vs2ZZ5g1syFURiSBV"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~cwjobs-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-cwjobs-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~cwjobs-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-cwjobs-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~cwjobs-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-cwjobs-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": "Start mode",
            "enum": [
              "search",
              "url"
            ],
            "type": "string",
            "description": "How to choose jobs. 'Search' builds listing pages from your keywords and locations. 'Direct URLs' takes CWJobs listing or job URLs you already have.",
            "default": "search"
          },
          "keywords": {
            "title": "Keywords",
            "type": "array",
            "description": "Search mode only. Job keywords or titles, one per line (e.g. 'javascript', 'devops', 'data engineer'). Leave empty to list every job in the chosen locations. Each keyword is combined with each location.",
            "items": {
              "type": "string"
            }
          },
          "locations": {
            "title": "Locations",
            "type": "array",
            "description": "Search mode only. Locations, one per line (e.g. 'london', 'manchester', 'remote'). Leave empty to search the whole UK. Each location is combined with each keyword.",
            "items": {
              "type": "string"
            }
          },
          "workType": {
            "title": "Work type",
            "enum": [
              "any",
              "permanent",
              "contract",
              "temporary"
            ],
            "type": "string",
            "description": "Filter by contract type. 'Any' applies no filter.",
            "default": "any"
          },
          "minSalary": {
            "title": "Minimum salary",
            "minimum": 0,
            "type": "integer",
            "description": "Only jobs paying at least this amount (in the salary period below). Leave empty for no salary filter."
          },
          "salaryPeriod": {
            "title": "Salary period",
            "enum": [
              "annual",
              "daily",
              "hourly"
            ],
            "type": "string",
            "description": "The period the minimum salary applies to.",
            "default": "annual"
          },
          "postedWithin": {
            "title": "Posted within",
            "enum": [
              "any",
              "1",
              "3",
              "7",
              "14"
            ],
            "type": "string",
            "description": "Only jobs posted within this many days. 'Any time' applies no filter.",
            "default": "any"
          },
          "sortBy": {
            "title": "Sort by",
            "enum": [
              "relevance",
              "date"
            ],
            "type": "string",
            "description": "Result ordering. 'Most recent' sorts by posting date (newest first); 'Relevance' is the site default.",
            "default": "relevance"
          },
          "workFromHome": {
            "title": "Work from home only",
            "type": "boolean",
            "description": "Keep only jobs the site lists with work-from-home options. In search mode this adds the site's own work-from-home filter to every search; in URL mode add it to the URL yourself.",
            "default": false
          },
          "urls": {
            "title": "Job or listing URLs",
            "type": "array",
            "description": "URL mode only. CWJobs listing URLs (e.g. https://www.cwjobs.co.uk/jobs/javascript/in-london, with optional ?page=N and filter parameters such as radius, postedwithin, sort) or direct job URLs (e.g. https://www.cwjobs.co.uk/job/...-job12345678). Multi-URL supported. Leaving the field untouched scrapes one example of each supported listing shape. Filter fields above are ignored in URL mode; pagination starts at the page in the URL and walks forward, keeping your query parameters.",
            "default": [
              "https://www.cwjobs.co.uk/jobs/javascript/in-london",
              "https://www.cwjobs.co.uk/jobs/javascript/in-london?page=2",
              "https://www.cwjobs.co.uk/jobs/javascript",
              "https://www.cwjobs.co.uk/jobs/in-london",
              "https://www.cwjobs.co.uk/jobs/permanent/javascript/in-london",
              "https://www.cwjobs.co.uk/jobs/javascript/in-london?page=2&radius=20&postedwithin=7",
              "https://www.cwjobs.co.uk/jobs/javascript/in-london?action=facet_selected&sort=2",
              "https://www.cwjobs.co.uk/job/ai-software-engineer-azure-co-pilot-python-c-net-javascript/carrington-recruitment-solutions-limited-job108250714"
            ],
            "items": {
              "type": "string"
            }
          },
          "fetchDetails": {
            "title": "Fetch full job details",
            "type": "boolean",
            "description": "Open each job page for the full description, GPS coordinates, posting and expiry dates, work type, and the rich employer profile (company size, founded year, industry, total jobs, video). Turn off for a faster, leaner run that returns only the fields visible on the listing page.",
            "default": true
          },
          "descriptionFormat": {
            "title": "Description format",
            "enum": [
              "html",
              "text",
              "markdown"
            ],
            "type": "string",
            "description": "How the job description is returned: HTML as published (default), plain text, or Markdown. Only affects output; change detection is unaffected.",
            "default": "html"
          },
          "descriptionMaxLength": {
            "title": "Description max length",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "Truncate each description to this many characters after formatting. 0 keeps the full text.",
            "default": 0
          },
          "compact": {
            "title": "Compact output",
            "type": "boolean",
            "description": "Drop the nested employer profile object and the listing snippet from each row for smaller datasets. Flat employer fields stay.",
            "default": false
          },
          "maxPages": {
            "title": "Max listing pages per search",
            "minimum": 0,
            "type": "integer",
            "description": "How many listing pages to walk per search or URL (about 25 jobs per page). Leave at 0 to walk the whole catalogue: the run stops at Max jobs, the site's own last page, or a repeat-page guard, not an artificial page cap.",
            "default": 0
          },
          "maxListings": {
            "title": "Max jobs",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum number of jobs to scrape across all searches. This is the main limit. Set 0 for unlimited (bounded by Max listing pages, if set). In Incremental mode this counts jobs SCANNED, not jobs returned — otherwise a quiet recurring run with nothing changed would page deeper to backfill this many rows and cost would never drop.",
            "default": 20
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "CWJobs requires UK residential connections. Apify Residential (United Kingdom) is recommended. Datacenter connections are refused by the site, so use residential. The actor rotates connections, falls back across countries, and uses a backup route if the primary keeps getting refused.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ],
              "apifyProxyCountry": "GB"
            }
          },
          "resumeFromRunId": {
            "title": "Resume from a previous run (delta)",
            "type": "string",
            "description": "Optional. ID of a previous run of this actor (or a dataset ID). Jobs already in that dataset are skipped, so this run returns only NEW jobs (a delta). Combine both runs' datasets for the full set. Max jobs then counts only the new jobs. For recurring daily/weekly 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 filter/detail setup; use State key when you want to name or deliberately share a monitoring campaign. 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 filter/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 / Max listing pages 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 to exclude from UPDATED change detection, in addition to the built-in handling (scrape time, relative posted age and the top-job / free-listing ad flags never trigger a false UPDATED). Use it when another field is noisy in your setup. Dotted names reach nested fields, for example salary.min. Ignored fields stay in the output; they just no longer make a job UPDATED. Changing this list never re-bills your tracked jobs.",
            "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}