{
  "openapi": "3.0.1",
  "info": {
    "title": "Remote Jobs Scraper",
    "description": "Remote job aggregator and remote job boards scraper: RemoteOK, WeWorkRemotely and more remote job websites, deduped, no API key. One remote job search across remote job sites for remote jobs worldwide, salary where listed. New remote jobs alerts to a remote jobs Slack digest. Remote jobs, one feed.",
    "version": "0.1",
    "x-build-id": "nnwYeAWNFtGev54i2"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/flash_scraper~remote-job-aggregator/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-flash_scraper-remote-job-aggregator",
        "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~remote-job-aggregator/runs": {
      "post": {
        "operationId": "runs-sync-flash_scraper-remote-job-aggregator",
        "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~remote-job-aggregator/run-sync": {
      "post": {
        "operationId": "run-sync-flash_scraper-remote-job-aggregator",
        "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": {
          "searchTerms": {
            "title": "Search terms",
            "type": "array",
            "description": "Keywords, case-insensitive. This form starts in WIDE mode (Match descriptions too is ticked): a term matches the job title, its tags or the first 500 characters of its description. Untick it for title-only precision. A job matches if ANY term matches (OR). Multi-word terms need every word ('python developer' requires both) — prefer short terms like 'python'. Empty = every job. Capped at 10 terms per run - extras beyond the first 10 are ignored.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "matchDescriptions": {
            "title": "Match descriptions too",
            "type": "boolean",
            "description": "ON (this form starts wide): searchTerms match the job title, its tags and the first 500 characters of its description — exactly what ships in `tags` and `description_snippet`, so you can always see why a row matched. Measured 2026-08-15 with ['python']: 194 rows wide vs 28 title-only. OFF = title-only, the precision opt-out: every hit visibly carries your keyword in the title, but a Python job titled 'Backend Engineer' is skipped. API and scheduled inputs that omit this field stay title-only (the stored default is false), so existing runs and their onlyNewJobs memories do not change. DevITjobs publishes no descriptions, so there wide mode adds its technology tags only.",
            "default": false
          },
          "boards": {
            "title": "Boards",
            "type": "array",
            "description": "Which boards to sweep. Default: the ten core boards. One more is opt-in and joins only when you add it here: Arbeitnow (Germany-centric, remote rows only, a handful of rows per run). Adding a board to a scheduled onlyNewJobs search starts a fresh baseline for that search. Results are deduplicated across boards, so selecting more boards never means paying twice for the same role.",
            "items": {
              "type": "string",
              "enum": [
                "remoteok",
                "weworkremotely",
                "working_nomads",
                "devitjobs_us",
                "devitjobs_uk",
                "muse",
                "remotive",
                "jobicy",
                "himalayas",
                "hn_hiring",
                "arbeitnow"
              ],
              "enumTitles": [
                "Remote OK",
                "We Work Remotely",
                "Working Nomads",
                "DevITjobs US (remote rows only)",
                "DevITjobs UK (remote rows only)",
                "The Muse (remote filter)",
                "Remotive",
                "Jobicy",
                "Himalayas",
                "Hacker News \"Who is hiring?\" (remote posts only)",
                "Arbeitnow (opt-in — Germany-centric, remote rows only)"
              ]
            },
            "default": [
              "remoteok",
              "weworkremotely",
              "working_nomads",
              "devitjobs_us",
              "devitjobs_uk",
              "muse",
              "remotive",
              "jobicy",
              "himalayas",
              "hn_hiring"
            ]
          },
          "maxItems": {
            "title": "Max jobs",
            "minimum": 1,
            "maximum": 4000,
            "type": "integer",
            "description": "Maximum number of deduplicated jobs to return across all boards. Sources are paginated to their real limits (The Muse ~1,980 rows, Himalayas ~2,000, Jobicy 200 per call), so large orders are reachable - but depth is source-limited, not unlimited.",
            "default": 100
          },
          "includeDescription": {
            "title": "Include full descriptions",
            "type": "boolean",
            "description": "ON: every row's `description` column carries the job's full HTML-stripped description text (rows get heavy). OFF (default): `description` carries the first 2,000 characters (visibly cut with …) alongside the always-on 500-character `description_snippet`. Boards that publish no description (DevITjobs) deliver null either way.",
            "default": false
          },
          "onlyNewJobs": {
            "title": "Only new jobs",
            "type": "boolean",
            "description": "Deliver only postings this exact search (same searchTerms + boards + filters) has never delivered before. The first run is the baseline — it delivers everything (up to maxItems; the matching postings it leaves behind stay eligible: the next runs read the boards back to the oldest one, at most 30 days, and deliver exactly those postings alongside new ones — never the same row twice within the 90-day memory, and never a merely newly-reachable posting dated more than two days before the last onlyNewJobs run of this search that delivered rows). A run where nothing is new delivers 0 rows, reports 'Nothing new since <last run>' and bills no results. Memory lives in a named key-value store ('remote-job-monitor') in YOUR Apify account, one record per search, entries pruned after 90 days.",
            "default": false
          },
          "postedWithinDays": {
            "title": "Posted within (days)",
            "minimum": 0,
            "maximum": 365,
            "type": "integer",
            "description": "Drop postings whose posted_at is older than this many days. The console form starts at 90 (measured: The Muse returned postings up to 869 days old); 0 (default) = off, no limit. With this filter alone and onlyNewJobs off, each board stops once it has about 3x maxItems matching rows (at most 4,000) instead of collecting up to 4,000; a board where few rows match still pages until it finds them, and Himalayas (whose feed holds only postings under 60 days old) stops at maxItems when this is 60 or more. An onlyNewJobs run always reads each board to full feed depth. The boards publish posted_at on ~100% of rows; a row with no date is kept unless strictFilters is on.",
            "default": 0
          },
          "salaryMinAnnual": {
            "title": "Minimum annual salary",
            "minimum": 0,
            "type": "integer",
            "description": "Drop postings whose annualized salary_max (salary_min when no max is published) is below this number. 0 (default) = off. Hourly / daily / weekly / fortnightly / monthly figures are already annualized upstream. The floor is CURRENCY-AGNOSTIC: the number is compared as the board published it (a 60,000 EUR row passes a 50,000 floor; use salary_currency downstream to tell them apart). Rows with no salary (~65% of a default run) are kept unless strictFilters is on.",
            "default": 0
          },
          "countries": {
            "title": "Countries",
            "type": "array",
            "description": "Keep postings whose location is open to ANY of these. ISO-2 codes or names: US / USA / United States / America, UK / GB / United Kingdom / England / Britain, EU / Europe / European Union, Canada, Germany, France, Spain, Netherlands, Poland, Portugal, India, Australia, Brazil, Mexico, plus the regions LATAM / Latin America, APAC, EMEA and Americas. A region matches its member countries both ways ('Europe' rows match a Germany filter and vice versa). Locations that are empty or say worldwide / anywhere / global / remote match any country. A location the actor cannot read (a bare city, as RemoteOK publishes) is kept unless strictFilters is on. Empty (default) = off.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "seniority": {
            "title": "Seniority",
            "type": "array",
            "description": "Keep postings whose derived `seniority` column is one of these. Derived from the board's own level field where it publishes one (Jobicy, Himalayas, The Muse, DevITjobs) and from title keywords otherwise ('Senior', 'Lead', 'Staff', 'Principal', 'Head of'…); a row whose seniority is unknown is kept unless strictFilters is on. Empty (default) = off.",
            "items": {
              "type": "string",
              "enum": [
                "entry",
                "junior",
                "mid",
                "senior",
                "lead",
                "staff",
                "principal",
                "manager",
                "director"
              ],
              "enumTitles": [
                "Entry level / intern / graduate",
                "Junior",
                "Mid level",
                "Senior",
                "Lead",
                "Staff",
                "Principal",
                "Manager",
                "Director (also VP / Head of / C-level)"
              ]
            },
            "default": []
          },
          "excludeKeywords": {
            "title": "Exclude keywords",
            "type": "array",
            "description": "Drop a posting when ANY of these terms appears in its title — and, when 'Match descriptions too' is on, in its tags or the first 500 characters of its description (the same fields searchTerms reads, so an exclusion is always visible in the delivered row). Case-insensitive substring; a term of up to 4 letters must stand as a whole word ('go' does not exclude 'Django'); a multi-word term is one phrase ('team lead'). Runs before the cap and the charge — an excluded row is never billed. Empty (default) = off. Setting it on an onlyNewJobs search starts a fresh baseline for that search.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "excludeCompanies": {
            "title": "Exclude companies",
            "type": "array",
            "description": "Drop a posting when its company names ANY of these terms (case-insensitive substring on the `company` column only; a term of up to 4 letters must stand as a whole word, so 'IBM' does not exclude 'IBMX'; a multi-word term is one phrase). Mute recruiting agencies or employers you have already applied to — no list ships with the actor, you name them. Runs before the cap and the charge — an excluded row is never billed. Empty (default) = off. Setting it on an onlyNewJobs search starts a fresh baseline for that search.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "strictFilters": {
            "title": "Strict filters",
            "type": "boolean",
            "description": "OFF (default): a row missing the data a filter needs — no posted date, no salary, an unreadable location, unknown seniority — is KEPT. ON: such rows are dropped as well. Does nothing unless at least one filter above is set.",
            "default": false
          },
          "webhookUrl": {
            "title": "Alert webhook URL (Slack, Discord, n8n, Make, Zapier…)",
            "type": "string",
            "description": "Optional. When at least one row is delivered, the run POSTs a digest to this URL: a Slack incoming webhook gets a text message, a Discord webhook gets a message, any other URL (n8n / Make / Zapier catch hook, your own endpoint) gets JSON with the counts, console links and the first 20 rows. Quiet runs send nothing. Pair it with onlyNewJobs on a schedule and this actor becomes an alert service on its own. Delivery is best-effort: a webhook failure never fails the run."
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}