# Seafarer Jobs Scraper — 4 Sources, 19K+ Listings (`solalab_digital/maritime-crewing-jobs-scraper`) Actor

Seafarer and maritime crewing vacancies from four boards (Martide, Crewell, FindSeaJobs, SeaCrew) in one normalised dataset, ~19,000 listings. Adds monthly-USD salary, rank tier, joining-date countdown and a dataCompleteness flag. Pick boards with sourcesToInclude. Each run writes an HTML dashboard.

- **URL**: https://apify.com/solalab\_digital/maritime-crewing-jobs-scraper.md
- **Developed by:** [Sankov Vadim](https://apify.com/solalab_digital) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Maritime Crewing & Seafarer Jobs Scraper

Four public seafarer job boards — **Martide**, **Crewell**, **FindSeaJobs** and **SeaCrew** — read in one run and returned as a single normalised dataset of roughly **19,000 live vacancies**: rank, vessel type, employer, wage, contract length and joining date, with the same column meaning the same thing whichever board a row came from.

> **Disclaimer.** This is an **unofficial, independent job aggregator**. It is **not affiliated with, endorsed by, sponsored by, or connected to** Martide, Crewell, FindSeaJobs, SeaCrew, or any employer listed on those sites. Their names are used here only to identify the publicly accessible websites this Actor reads. All job content belongs to its respective publishers.

### What this Actor does

Maritime crewing demand is split across boards that do not talk to each other. A recruiter who wants a view of the market has to read four sites, each with its own field names, its own salary formats and its own pagination. This Actor collapses that into one export.

1. It crawls each selected board's listing pages in order, following pagination **dynamically** — it keeps going until a page returns no job cards, so it adapts as a board grows or shrinks. No page count is hard-coded.
2. On Martide, FindSeaJobs and SeaCrew it opens **every individual job page** and extracts the full record, not just the summary card. Crewell is read from its listing cards only, for the reason given below.
3. It maps all four sources onto **one shared record shape**, so `rank`, `salaryMin` and `joiningDate` mean the same thing in every row. Fields that only one board publishes are not thrown away — they survive under `siteSpecificExtra`.
4. It **enriches** each record with derived fields no board publishes: a monthly-USD wage, a seniority tier and a real calendar joining date.
5. It marks each record's `dataCompleteness` so you can always tell a full detail-page extraction from a listing-card one.
6. It offers a cross-board `possibleDuplicateKey`, because manning agents cross-post the same berth to several boards.
7. When the run finishes it builds an **HTML run report** and saves it to the run's key-value store.

#### The four sources

| Source | Site | Approx. live vacancies | Extraction | Notes |
|---|---|---|---|---|
| Martide | `martide.com` | ~130 | Full detail pages | Richest per-record data: vessel name, DWT, engine type, trading area |
| Crewell | `crewell.net` | ~3,600 | Listing cards only | `robots.txt` declares `Crawl-delay: 10`; see below |
| FindSeaJobs | `findseajobs.com` | ~7,300 | Full detail pages | Bot-protection CDN; may need a proxy on some IPs — see below |
| SeaCrew | `seacrew.net` | ~8,500 | Full detail pages | Also publishes vessel flag, view and applicant counts |

Use the **`sourcesToInclude`** input to pick which of the four actually run. Every source you include multiplies the number of records, and therefore the cost, of a full crawl — so deselecting the boards you do not need is the main cost control here.

#### Why Crewell is listing-only

Crewell's `robots.txt` publishes `Crawl-delay: 10`. Honouring it means one request every ten seconds, so opening a detail page for each of ~3,600 vacancies would be a ten-hour crawl; reading the 180 listing pages alone takes about half an hour. The same `robots.txt` also disallows `/*/vacancies/details/` and annotates it as an anti-scraping honeypot — every Crewell listing page carries exactly one link into that trap, and this Actor rejects the path explicitly rather than relying on link-following luck.

The cards are rich enough that this costs little: rank, vessel type, salary, joining window, contract length, employer name and employer profile URL are all on the card. Crewell records therefore carry `dataCompleteness: "LISTING_ONLY"`, so you can see what was traded away instead of having to infer it. Records from the other three sources carry `"FULL"`.

#### FindSeaJobs and proxies

FindSeaJobs sits behind a bot-protection CDN that rejects some datacentre IP ranges. In practice that means:

- From an Apify platform run it normally works with **no proxy at all** — verified on a real run.
- From some other IPs the CDN returns `403` on every path, `robots.txt` included.

So no proxy is configured by default. If a run reports zero records from FindSeaJobs while the other boards return data, re-run with a **residential** proxy group in `proxyConfiguration`. Either way a blocked source never fails the run: it logs the skip and the other three complete normally, and you are not billed for a source that returned nothing.

#### How it treats the source sites

- `robots.txt` is fetched and honoured for every source on every request.
- Each site's own published limit wins where it is stricter than your `maxRequestsPerMinute`. Crewell is never crawled faster than 6 requests per minute regardless of the input.
- The rate limit is applied **per source**, expressed as a rate rather than a sleep, so one board's politeness rule does not set the pace for the other three.
- Only publicly accessible pages are read. There is **no login, no paywall circumvention and no bot-protection evasion** of any kind.
- Martide's `robots.txt` carries a `Content-Signal: ai-train=no` preference. This Actor produces a structured job dataset for recruitment and market-analysis use; it is not an AI training-corpus collector, and buyers should respect that signal in their own downstream use.

### Input parameters

Every parameter is optional. With no input at all, the Actor pulls a small 60-record sample so a plain run finishes in well under a minute — set `maxItems` to whatever size you actually want (1,000, 2,000, 10,000, or `0` for every job currently listed across all four boards, ~19,000 and well over five minutes to run).

| Parameter | Type | Default | Description |
|---|---|---|---|
| `sourcesToInclude` | array | all four | Which boards to crawl: `martide`, `crewell`, `findseajobs`, `seacrew`. |
| `maxItems` | integer | `60` | Stop the **whole run** after this many records across all sources combined. `0` means no limit. A hard spend cap. |
| `maxPages` | integer | `0` | Limit listing pages **per source**. With 4 sources and `maxPages=5`, up to 20 listing pages are visited in total. `0` means follow pagination until exhausted. |
| `startPage` | integer | `1` | 1-based listing page each source begins from. Raise it only to resume or to split a crawl across runs. |
| `includeJobDescription` | boolean | `true` | Include the free-text description and its original HTML. Turn off for a tighter tabular export. |
| `maxRequestsPerMinute` | integer | `60` | Courtesy rate limit applied to each source separately. A site's own stricter limit still wins. |
| `proxyConfiguration` | object | disabled | Usually unnecessary. A residential group is the fallback if FindSeaJobs returns nothing. |

Note the asymmetry: **`maxItems` is global, `maxPages` is per source.** A spend cap that silently quadrupled with the number of sources would not be a cap.

#### Example input

```json
{
  "sourcesToInclude": ["martide", "crewell", "findseajobs", "seacrew"],
  "maxItems": 500,
  "includeJobDescription": true,
  "maxRequestsPerMinute": 60
}
```

### Output

One dataset item per vacancy, on a **stable column set that does not depend on which sources a run included** — a CSV whose columns shift with the input is not a usable export. Fields a given board does not publish are returned as `null` rather than omitted.

#### Shared fields

| Field | Type | Description |
|---|---|---|
| `sourceSite` | string | `martide`, `crewell`, `findseajobs` or `seacrew`. Part of the public contract; these values will not be renamed. |
| `sourceUrl` | string | Canonical job page on the originating board. |
| `sourceListingId` | string | The board's own id for the listing. Unique **per source**, not globally — two boards legitimately reuse numbers. |
| `dataCompleteness` | string | `FULL` (built from a detail page) or `LISTING_ONLY` (built from a listing card). |
| `scrapedAt` | string | ISO 8601 timestamp of the extraction. |
| `title` / `rank` | string | Combined posting title, and the seafarer rank on its own. |
| `companyName` | string | Hiring company or manning agent. |
| `vesselType` / `vesselName` | string | e.g. `Container Ship`, `Oil/Chemical Tanker`; specific vessel when disclosed. |
| `salaryMin` / `salaryMax` | number | Wage bounds. Equal when a single figure is advertised, distinct for ranges. |
| `salaryCurrency` / `salaryPeriod` | string | e.g. `USD`, `MONTH`. Both `null` when no wage is disclosed, so a currency is never reported without an amount. |
| `contractLengthValue` / `contractLengthUnit` | number / string | e.g. `4`, `MONTHS`. |
| `joiningDate` | string | Requested embarkation date exactly as the board published it. |
| `datePosted` | string | ISO 8601 publication timestamp, where the board publishes one. |
| `siteSpecificExtra` | object | Everything one board publishes and the others do not. See below. |
| `possibleDuplicateKey` | string | Cross-board cross-post signal. See below. |

#### `siteSpecificExtra`

Flattening four boards to a lowest common denominator would throw away most of what makes each one worth paying for, so anything outside the shared shape lands here instead of being dropped: Martide's deadweight tonnage, engine type and trading area; SeaCrew's vessel flag, view and applicant counts; FindSeaJobs' tags and department; Crewell's joining *window*. A parser gaining a new field never silently loses it.

#### Derived fields

Boards publish wages in whatever period the employer chose, ranks as free text, and joining dates often with no year. These five fields are computed from the scraped record so you can sort and filter the export directly, without writing a normalisation pass of your own.

| Field | Type | Description |
|---|---|---|
| `monthlyEquivalentUsd` | number | The published wage restated as a monthly USD figure — daily rates × 30, weekly × 52/12, annual ÷ 12, monthly unchanged. A published range is reduced to its midpoint. `null` when no wage is published or when it is not in USD. |
| `salaryNormalization` | string | Why the wage above is or is not present: `NORMALIZED_USD`, `NO_SALARY_PUBLISHED`, `NON_USD_NOT_CONVERTED` or `UNSUPPORTED_PAY_PERIOD`. Lets you separate "this employer pays nothing we can see" from "we could not convert this currency". |
| `rankTier` | string | Coarse seniority bucket — `Senior`, `Mid-level`, `Junior / Support` or `Unclassified` — matched on the rank text. A heuristic, not an official maritime grade. |
| `joiningDateIso` | string | `joiningDate` resolved to a real `YYYY-MM-DD` date. Boards print "13 September" with no year; the year is inferred from `datePosted`. `null` when the text cannot be parsed safely (`ASAP`, `TBC`, and similar). |
| `daysUntilJoining` | integer | Days from `scrapedAt` to `joiningDateIso`. Sort ascending for the most urgent vacancies. Negative values mean the join date has already passed and the listing is stale. |

Three deliberate omissions, so you know what the numbers do and do not claim:

- **No currency conversion.** Non-USD wages keep their original figures and are flagged `NON_USD_NOT_CONVERTED`. A published Actor runs for months; a fixed exchange-rate table baked into it would quietly drift and present a stale guess as confidently as a scraped fact.
- **No invented dates.** Where the joining text is ambiguous, `joiningDateIso` and `daysUntilJoining` are `null` rather than a guess.
- **No cross-source inference.** A field one board omits is never filled in from another board's posting, even when `possibleDuplicateKey` suggests they are the same berth.

#### Cross-posting: `possibleDuplicateKey`

Manning agents cross-post: the same berth appears on two or three of these boards under different ids, different wording and a salary quoted a couple of hundred dollars apart. `possibleDuplicateKey` hashes the identifying parts — employer, vessel type and rank normalised, salary rounded to a bucket, joining date reduced to its ISO week — so cross-posts of one berth collide.

**This Actor never de-duplicates on it.** Two matching keys are strong evidence of a cross-post, not proof: sister ships of one operator can legitimately collide. Dropping a real vacancy is worse for a recruiter than seeing two rows, and only you know which way that trade goes for your use. So the key is exported and the decision is yours — `GROUP BY possibleDuplicateKey` to collapse, or ignore the column. It is `null` where too little identifying data is present to make any claim at all.

Within a single source, exact re-appearances **are** de-duplicated during the crawl by `sourceListingId`, so a vacancy that shifts between listing pages mid-run is saved, and charged, only once.

#### Dataset views

The dataset ships with four views, selectable in the Console's dataset tab and in exports:

- **Overview** — the columns you want when scanning vacancies.
- **Compensation** — wage fields plus `monthlyEquivalentUsd`, `salaryNormalization` and `rankTier`.
- **Cross-posting** — `possibleDuplicateKey` alongside the fields that feed it.
- **Source & completeness** — provenance, `dataCompleteness`, and `siteSpecificExtra`.

### Run report (HTML dashboard)

Every run writes a self-contained HTML report to the run's key-value store under the key **`DASHBOARD`**. The Actor logs its URL at the end of the run log, and you can also reach it from the run's **Storage → Key-value store** tab. Open it in a browser — no download, no spreadsheet, no notebook.

The report shows:

- **Run summary tiles** — jobs scraped, how many published a wage and what share of the run that is, the average monthly USD wage across those, and the number of distinct employers, vessel types and rank tiers.
- **Wage distribution** — a histogram of `monthlyEquivalentUsd`, so you can see the shape of the market rather than just its mean.
- **Top 10 employers by vacancies** — which manning agents are hiring hardest this run.
- **Vacancies by vessel type**, **by rank tier**, **by source site** and **by data completeness**.

Practical details:

- It is one HTML file with the data embedded. Nothing is fetched from a server of ours, and the file can be saved, emailed or attached to a report as-is.
- Every chart has a plain data table underneath it, behind a **Show data table** toggle. The numbers stay readable if charts do not render, and screen readers get real table markup rather than a canvas.
- It follows your system light/dark setting, and it is readable on a phone.

### Why this rather than a bare-JSON scraper

Most job-board Actors stop at the raw dump of a single site: unnormalised fields, a thin README, and whatever that site happened to render. The work of making it usable — reading three more boards, reconciling their field names, converting wage periods, resolving year-less dates, bucketing free-text ranks, spotting cross-posts, then building a chart to see what you actually got — lands on you, every run.

| | Typical job-board Actor | This Actor |
|---|---|---|
| Coverage | One board | Four boards, ~19,000 listings, selectable per run |
| Field names | Whatever the site used | One shared shape across all sources, stable column set |
| One-board-only fields | Dropped, or left in an ad-hoc blob | Preserved under `siteSpecificExtra` |
| Wage fields | Whatever string the site printed | Parsed bounds, currency and period, **plus** a monthly USD equivalent with an explicit status flag |
| Rank | Free text | Free text **plus** a seniority tier |
| Joining date | `"13 September"` | Same, **plus** a real ISO date and a days-out countdown |
| Cross-posts between boards | Invisible | `possibleDuplicateKey`, exported and left to you |
| Missing data | Field absent, or a silent `0` | Explicit `null` with a reason code |
| Extraction depth | Unstated | `dataCompleteness` on every record |
| Seeing your results | Open the JSON | An HTML report linked from every run |

### Who this is for

#### Crewing and recruitment agencies

A crewing agency staffing container vessels needs to know what the market is paying **this week**, not last quarter — and across the whole market, not one board. Run this on a daily or weekly schedule and the dataset becomes a live wage benchmark: filter to `rank = "Chief Engineer"` and `vesselType = "Container Ship"`, sort by `monthlyEquivalentUsd`, and you can see whether your own offer is competitive before a candidate turns it down. Because that field puts daily, weekly and monthly adverts on one scale, a day-rate posting no longer looks cheap next to a monthly one. And because `salaryNormalization` is explicit, you benchmark against published wages only instead of silently averaging zeros.

The same dataset supports four further workflows:

- **Competitor monitoring.** Group by `companyName` to see which manning agents are hiring, for which vessel types, and how aggressively they are pricing — including which of them work which boards.
- **Demand forecasting.** `joiningDateIso` and `contractLengthValue` together show when crew will rotate off and when the next hiring wave lands. Sort by `daysUntilJoining` to surface the berths that need filling first.
- **Candidate matching.** `rank`, `rankTier` and `vesselType` let you match a bench of available seafarers to live vacancies in seconds rather than reading postings one by one.
- **Board coverage analysis.** Group by `sourceSite` and `possibleDuplicateKey` to see how much of each board is unique and how much is cross-posted — which tells you which boards are worth your own recruiters' time.

#### Maritime market analysts and job boards

Track wage movement by rank and vessel type over time across four sources at once, or enrich an existing vacancy aggregator with a feed that arrives already normalised and already flagged for cross-posting.

### Pricing

This Actor uses Apify's **pay-per-event** model. One event, `job-scraped`, is charged once per job record saved to the dataset. You pay nothing for listing pages that yield no data, nothing for a source that is skipped, and nothing for retries.

Two consequences worth knowing:

- **You are never billed twice for the same listing.** Records are de-duplicated by `sourceListingId` within each source during the crawl, so a vacancy that shifts between listing pages mid-run is saved, and charged, once.
- **`maxItems` is a hard spend cap.** Set it and the run stops at exactly that many records across all sources combined. The counter is guarded against the four concurrent source crawlers, so it cannot overshoot.

### Frequently asked questions

#### How current is the data?

Every run reads the live sites. `datePosted` comes from the posting itself where the board publishes it, and `scrapedAt` records when the extraction happened, so you can always tell how fresh a record is.

#### Why do some records have less data than others?

Check `dataCompleteness`. `FULL` records were built from an individual job page. `LISTING_ONLY` records — every Crewell record — were built from the listing card, which carries rank, vessel type, salary, joining window, contract length and employer but not the detail-page fields. The flag is on every row precisely so this is never a guess.

#### Why is the salary missing on some jobs?

Because the employer did not publish one. Those records carry null wage fields and `salaryNormalization: "NO_SALARY_PUBLISHED"`, rather than a misleading zero. The run report shows what share of the run that was, so you always know how much of the market you are benchmarking against.

#### How accurate is `rankTier`?

It is a heuristic. These boards publish ranks as free text with no seniority taxonomy behind them, so the tier is matched on rank keywords — command ranks to `Senior`, certificated officers and engineers to `Mid-level`, ratings, cadets and catering crew to `Junior / Support`. Anything the keywords do not cover returns `Unclassified` rather than being forced into a bucket. Use it to slice a dataset quickly; use `rank` itself when the exact grade matters.

#### Can I run only one board?

Yes. Set `sourcesToInclude` to just that source. It is also the cheapest way to sample: one board, `maxItems: 20`.

#### Where do I find the HTML report?

At the end of the run log, and in the run's **Storage → Key-value store** tab under the key `DASHBOARD`. It is a single file you can open, save or forward.

#### Can I run this on a schedule?

Yes. Use Apify Schedules to run it daily or weekly, and de-duplicate on `sourceSite` + `sourceListingId` to maintain a rolling database of live vacancies.

#### Does it need a proxy?

Normally no. All four boards have been crawled from an Apify platform run with no proxy configured. Martide, Crewell and SeaCrew serve plain server-rendered HTML with no bot protection at all; FindSeaJobs has a CDN in front of it that refuses some IP ranges, so a **residential** group is the fallback if that one board comes back empty.

### Legal and responsible use

This Actor collects only publicly available job postings. It does not collect seafarer personal data, does not access any account-protected area, and does not bypass authentication or bot protection on any of the four sources.

Job listing content remains the property of the publishers. You are responsible for ensuring your use of the data complies with applicable law — including data protection rules in your jurisdiction — and with each source site's own terms. If you intend to republish scraped listings, review those terms first.

# Actor input Schema

## `sourcesToInclude` (type: `array`):

Which job boards to crawl. All four are included by default, which is roughly 19,000 live vacancies in total. Deselect sources you do not need: every source you include multiplies the number of records — and therefore the cost — of a full run. Rough sizes: Martide ~130, Crewell ~3,600, FindSeaJobs ~7,300, SeaCrew ~8,500.

## `maxItems` (type: `integer`):

Stop the whole run after this many job records have been saved across all sources combined. Set to 0 to scrape every job currently listed (~19,000 across all four boards — a full run takes well over Apify's 5-minute automatic health-check window, so leave this capped unless you specifically want the full pull). Useful for capping cost on a paid run, for a quick sample before a full crawl, or for picking a specific size like 1,000/2,000/10,000.

## `maxPages` (type: `integer`):

Limit how many listing pages are visited on each included source. Set to 0 for no limit, in which case the Actor keeps following pagination until a page returns no job cards. This limit is per source, not shared: with 4 sources and maxPages=5, up to 20 listing pages are visited in total. Jobs per page: Martide ~50, Crewell 20, FindSeaJobs 20, SeaCrew 30.

## `startPage` (type: `integer`):

The 1-based listing page each source begins from. Leave at 1 for a normal run; raise it only to resume or to split a large crawl across several runs. Applied to every included source alike.

## `includeJobDescription` (type: `boolean`):

Include the free-text job description and its original HTML in each record. Turn this off for a smaller, tabular dataset that is easier to load into a spreadsheet or CRM.

## `maxRequestsPerMinute` (type: `integer`):

Courtesy rate limit applied to each source separately. Each site's own published limit still wins where it is stricter: Crewell declares a 10-second crawl-delay in its robots.txt and is never crawled faster than 6 requests per minute regardless of this setting. Lower this if you run the Actor frequently.

## `proxyConfiguration` (type: `object`):

Usually not needed. Martide, Crewell and SeaCrew serve plain server-rendered HTML with no bot protection. FindSeaJobs sits behind a CDN that rejects some datacentre IP ranges; on Apify's platform it normally works with no proxy, but if it returns no records, re-run with a RESIDENTIAL proxy group. A blocked source never fails the run — the other three complete normally.

## Actor input object example

```json
{
  "sourcesToInclude": [
    "martide",
    "crewell",
    "findseajobs",
    "seacrew"
  ],
  "maxItems": 60,
  "maxPages": 0,
  "startPage": 1,
  "includeJobDescription": true,
  "maxRequestsPerMinute": 60,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

Every scraped vacancy as a normalised 25-column record, one row per posting across Martide, Crewell, FindSeaJobs and SeaCrew. Download as JSON, CSV, XLSX or read via the API.

## `dashboard` (type: `string`):

Standalone HTML report for the run: salary distribution by rank tier, source breakdown, joining-date urgency and the full vacancy table. Open it in a browser.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "sourcesToInclude": [
        "martide",
        "crewell",
        "findseajobs",
        "seacrew"
    ],
    "maxItems": 60,
    "maxPages": 0,
    "startPage": 1,
    "maxRequestsPerMinute": 60
};

// Run the Actor and wait for it to finish
const run = await client.actor("solalab_digital/maritime-crewing-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "sourcesToInclude": [
        "martide",
        "crewell",
        "findseajobs",
        "seacrew",
    ],
    "maxItems": 60,
    "maxPages": 0,
    "startPage": 1,
    "maxRequestsPerMinute": 60,
}

# Run the Actor and wait for it to finish
run = client.actor("solalab_digital/maritime-crewing-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sourcesToInclude": [
    "martide",
    "crewell",
    "findseajobs",
    "seacrew"
  ],
  "maxItems": 60,
  "maxPages": 0,
  "startPage": 1,
  "maxRequestsPerMinute": 60
}' |
apify call solalab_digital/maritime-crewing-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solalab_digital/maritime-crewing-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/b46sIc85dmoeL23wk/builds/yS4BQ1plloye4Gnzu/openapi.json
