{
  "openapi": "3.0.1",
  "info": {
    "title": "Yacht Crew Jobs Scraper: Yacrew, Meridian, Crew Network",
    "description": "Superyacht crew vacancies from Yacrew, Meridian and The Crew Network in one normalised dataset: department, seniority tier, yacht length, rotation, start month and monthly-pay restatement. Pick boards, exclude expired listings by default, get only what's new. Each run writes an HTML dashboard.",
    "version": "0.1",
    "x-build-id": "kty0PHVCDhEREAIV5"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/solalab_digital~yacht-crew-jobs-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-solalab_digital-yacht-crew-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/solalab_digital~yacht-crew-jobs-scraper/runs": {
      "post": {
        "operationId": "runs-sync-solalab_digital-yacht-crew-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/solalab_digital~yacht-crew-jobs-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-solalab_digital-yacht-crew-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": {
          "sources": {
            "title": "Source sites to scrape",
            "uniqueItems": true,
            "type": "array",
            "description": "Which yacht crew boards to crawl. All three are included by default. Each board is crawled independently: one blocked or broken site never fails the run, it is reported as FAILED in the run summary while the others finish. Rough sizes: Yacrew — 23,641 job pages in its sitemap, of which only the newest few hundred are still open; Meridian — about 556 listings across 56 list pages; The Crew Network — 75 listings reported by its public jobs API. Leave a source out to cut both run time and cost.",
            "items": {
              "type": "string",
              "enum": [
                "yacrew",
                "meridian",
                "crewnetwork"
              ],
              "enumTitles": [
                "Yacrew (yacrew.com) — largest board, live + archive",
                "Meridian (meridiango.com) — no posting dates, see firstSeenAt",
                "The Crew Network (crewnetwork.com) — yacht names and lengths"
              ]
            },
            "default": [
              "yacrew",
              "meridian",
              "crewnetwork"
            ]
          },
          "includeArchive": {
            "title": "Include expired listings",
            "type": "boolean",
            "description": "Also return vacancies that no longer accept applications. Applies to Yacrew only — it is the one board that keeps its expired pages online, and they outnumber the open ones by more than an order of magnitude. Turning this on therefore multiplies both the run time and the number of billed records, so raise maxItems deliberately rather than by accident. Expired records are marked isArchived = true and are useful as salary history; for a current hiring picture leave this off.",
            "default": false
          },
          "maxItems": {
            "title": "Maximum jobs to save",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on records saved across all sources combined, counted after the filters below. Set 0 for no cap. The cap is shared, not per source, and it is enforced exactly: the run never saves — or bills for — one record more. Leave it capped for a sample run; raise it once you know how many records your filters actually return.",
            "default": 60
          },
          "onlyNew": {
            "title": "Only listings not seen before",
            "type": "boolean",
            "description": "Skip listings that an earlier run of this Actor already saved, so a scheduled run returns just what appeared since last time. State is kept between runs under the state key in the advanced section. It detects new listings by their ID: an edit to a listing already saved is not a new listing and will not come back. Changing the filters resets the part of the state that depends on them, so a filter change can never hide a listing permanently.",
            "default": false
          },
          "categories": {
            "title": "Departments",
            "uniqueItems": true,
            "type": "array",
            "description": "Keep only vacancies in these departments. Leave empty for all. The department is normalised from each board's own category wording, so DECK means the same thing on all three. A listing whose category does not map to any of these values is returned only when the filter is empty — its raw category is always kept in siteSpecificExtra.",
            "items": {
              "type": "string",
              "enum": [
                "DECK",
                "ENGINEERING",
                "GALLEY",
                "INTERIOR",
                "SPECIALIST",
                "SHORE_BASED",
                "OTHER"
              ],
              "enumTitles": [
                "Deck",
                "Engineering",
                "Galley / chef",
                "Interior",
                "Specialist",
                "Shore based",
                "Other"
              ]
            },
            "default": []
          },
          "positionKeywords": {
            "title": "Position keywords",
            "maxItems": 20,
            "uniqueItems": true,
            "type": "array",
            "description": "Keep only vacancies whose position or job title contains one of these words (case-insensitive, OR logic). Example: chef, bosun, engineer. Matching the title as well as the position matters here: some adverts list two roles at once and then publish no single position value, so the role is only in the title. At most 20 entries of 60 characters each — the same limits the run enforces internally, listed here so a run over the limit fails at the form, not after it has started.",
            "items": {
              "type": "string",
              "maxLength": 60
            },
            "default": []
          },
          "regions": {
            "title": "Location keywords",
            "maxItems": 20,
            "uniqueItems": true,
            "type": "array",
            "description": "Keep only vacancies whose location text contains one of these words (case-insensitive, OR logic). Example: Mediterranean, Caribbean, Fort Lauderdale. The three boards publish location at different granularity — a region on one, a city on another, only inside the description text on the third — so this is a substring match, not a place database. Listings with no location text are excluded while this filter is set. At most 20 entries of 60 characters each, the same as position keywords above.",
            "items": {
              "type": "string",
              "maxLength": 60
            },
            "default": []
          },
          "postedWithinDays": {
            "title": "Posted within the last N days",
            "minimum": 1,
            "type": "integer",
            "description": "Keep only vacancies published in the last N days, measured from datePosted (or dateUpdated when that is what the board publishes). Leave empty for no date filter. Meridian publishes no dates at all: its listings are judged by firstSeenAt, the moment this Actor first saw them, and the ones whose firstSeenAt is only a baseline from the very first run are excluded while this filter is set."
          },
          "minVesselLengthM": {
            "title": "Minimum yacht length",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "Keep only vacancies on yachts at least this long, in metres. Leave empty for no lower bound. Listings whose length could not be determined are excluded while this filter is set — a length filter that silently passed unchecked records would be worse than no filter. The run summary counts them separately under filtered.unknownVesselLength, and fieldCoverage.vesselLength reports how much of the run had a length at all."
          },
          "maxVesselLengthM": {
            "title": "Maximum yacht length",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "Keep only vacancies on yachts at most this long, in metres. Leave empty for no upper bound. Same rule as the minimum: listings with an unknown length are excluded while this filter is set."
          },
          "includeJobDescription": {
            "title": "Include job description text",
            "type": "boolean",
            "description": "Include the free-text description of each vacancy. Turn it off for a compact, spreadsheet-friendly table. The description is always plain text with contact details removed; the original HTML is never returned.",
            "default": true
          },
          "stateKey": {
            "title": "State key",
            "pattern": "^[A-Za-z0-9!_.'()-]{1,64}$",
            "type": "string",
            "description": "Names the set of already-seen listings that 'Only listings not seen before' reads and writes. Change it only to keep two schedules apart: two schedules with different filters sharing one state key would consume each other's new listings. It also drives firstSeenAt for boards that publish no posting date, so a fresh key means those listings are seen for the first time again. Letters, digits and !_.'()- only, up to 64 characters — this becomes part of an Apify key-value store key, and anything else would fail that source at storage time instead of at the form.",
            "default": "default"
          },
          "maxRequestsPerMinute": {
            "title": "Maximum requests per minute, per source",
            "minimum": 1,
            "maximum": 120,
            "type": "integer",
            "description": "Courtesy rate limit, applied to each source separately rather than shared. Whatever a site's own robots.txt declares still wins where it is stricter — The Crew Network publishes a 10-second crawl delay and is never requested faster than 6 times a minute regardless of this setting. Lower it if you run the Actor often.",
            "default": 40
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Usually not needed: all three sources serve plain HTML or a public JSON API with no challenge. Add a proxy only if a source starts returning no records from Apify's own IP ranges — the run summary reports a blocked source explicitly instead of silently returning an empty dataset.",
            "default": {
              "useApifyProxy": false
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}