# Trawle Job Search — Employer-Sourced Jobs API (`trawle/trawle-job-search`) Actor

Search employer-sourced jobs by keyword, location, remote work, seniority and employment type. Export structured jobs with salaries when available, original application links and checked dates. Official Trawle integration; no Trawle API key needed.

- **URL**: https://apify.com/trawle/trawle-job-search.md
- **Developed by:** [Trawle](https://apify.com/trawle) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 9.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Trawle Job Search — Employer-Sourced Jobs API

Search jobs from [Trawle](https://trawle.com) and export structured results into your workflow.
Filter by job-title or company keywords, where you want to work from, remote/hybrid/onsite work,
seniority, employment type and when Trawle first saw a listing.

This is the **official Trawle integration**. It queries Trawle's existing job index through the
[public API](https://developers.trawle.com), with no Trawle account or API key required.

### What can you do with it?

- Build a weekly jobs shortlist for a professional community or newsletter.
- Export matching jobs into Google Sheets, Airtable or a recruiting research workflow.
- Feed a career assistant with job titles, employer application links and available salary data.
- Monitor matching jobs once a day and deduplicate results using stable job IDs.

### Quick start

1. Choose a keyword and optional location. Select **Remote** if that is the workplace you want.
2. Start with 20 results to check relevance.
3. Run the Actor. Open **Jobs** to inspect the results or export JSON, CSV or Excel.
4. Open **Search and pagination** for match counts and continuation information.

#### Remote software roles from Denmark

```json
{
  "keyword": "software",
  "location": "Denmark",
  "workplace": "remote",
  "maxItems": 20
}
```

#### Senior data analyst roles

```json
{
  "keyword": "data analyst",
  "seniority": "senior",
  "maxItems": 50
}
```

#### Remote customer support jobs first seen this week

```json
{
  "keyword": "support",
  "workplace": "remote",
  "postedWithin": "7d",
  "maxItems": 50
}
```

### Input

| Field             | Meaning                                                                                                           | Default            |
| ----------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------ |
| `keyword`         | Words in the title, department or company name, using word-prefix matching                                        | No keyword filter  |
| `location`        | Exact place name, such as Denmark or Copenhagen                                                                   | No location filter |
| `workplace`       | `any`, `remote`, `hybrid`, `onsite`                                                                               | `any`              |
| `seniority`       | `any`, `intern`, `entry`, `mid`, `senior`, `lead`, `principal`, `director`, `vp`, `executive`                     | `any`              |
| `employment`      | `any`, `full_time`, `part_time`, `contract`, `temporary`, `internship`, `apprenticeship`, `seasonal`, `volunteer` | `any`              |
| `postedWithin`    | First seen by Trawle within `1d`, `3d`, `7d`, `30d`, or `any`                                                     | `any`              |
| `maxItems`        | Maximum unique job IDs to export, from 1 to 500                                                                   | 50                 |
| `placeIds`        | GeoNames integer IDs instead of `location`                                                                        | Unset              |
| `postedSince`     | ISO UTC timestamp for an incremental search                                                                       | Unset              |
| `cursor`          | Continuation cursor, with identical filters                                                                       | Unset              |
| `windowStartedAt` | Timestamp carried by `continuationInput`; do not invent it                                                        | Unset              |

The Console's example input selects remote work and 20 results; omitted API inputs use the
defaults above. Different filters are ANDed. Keywords do not search full descriptions or every
mentioned skill. Recruitment/staffing agencies are excluded by the underlying search by default.

#### Locations and unknown fields

An exact country or region takes precedence over a same-named city (for example, Denmark).
Other ambiguous place names produce an error listing candidate IDs. Replace `location` with,
for example, `"placeIds": [2623032]` for Denmark. You can also
[look up places](https://trawle.com/v1/places?q=Denmark). Remote does not mean worldwide; verify
residence, timezone, visa and other requirements on the employer's page before applying.

Specific seniority/employment filters exclude jobs without those classifications. Missing salary,
role, workplace or posting-date data stays `null`; the Actor does not guess missing values.

### Output

Each dataset row retains the public API's job summary:

- Stable UUID `id`, `title`, `company` (name, slug, domain, agency classification).
- Normalized `role`, `family`, `locations`, `workplace`, `remoteScope`, `seniority`, `employmentType`.
- `salary`, when available: `min`, `max`, `currency` and `period` as supplied by the employer.
- `firstSeenAt`, `lastSeenAt`, `postedAt`, `openSince`, `repostCount`, `closedAt`.
- `applyUrl` for the original employer posting and `url` for the job on Trawle.
- `dataDate` from the API, this export's `retrievedAt`, and `attribution`.

Full employer descriptions are not part of this export. Follow the source link to read the posting.
Job summaries use the same contract as [Trawle's OpenAPI specification](https://trawle.com/openapi.json).

The `OUTPUT` key-value record contains `resultCount`, `total`, `totalCapped`, the resolved `query`,
freshness metadata, `hasMore`, `nextCursor`, `nextSince` and `continuationInput`.
`total` is capped at 10,000 when `totalCapped` is true; it is not the export size.

#### Pagination and daily workflows

1. Run your search. If `hasMore` is true, use **the entire `continuationInput` object** as the input
   to the next run. This retains the filters and original search-window timestamp.
2. Repeat until `hasMore` is false. `nextSince` remains null while there are unexported pages.
3. Save the final non-null `nextSince`. For the next daily search, use your original filters with
   that value as `postedSince`, removing `cursor`, `windowStartedAt` and `postedWithin`.
4. Deduplicate across runs by `id`. The timestamp boundary is inclusive, and the live index can
   change between pages. Only the IDs within one run are deduplicated automatically.

A bare `cursor` without its original window timestamp cannot safely advance an incremental
watermark: `nextSince` will retain `postedSince`, or be null for a full-index search. Prefer the
provided `continuationInput`. Each run has a 500-result cap and a 10-page safety ceiling, and reports
remaining pages explicitly. The Actor does not persist a user's search window across scheduled runs.

For a simple daily schedule, a rolling `postedWithin: "3d"` window plus downstream ID deduplication
is also useful. Such a fixed rolling window does not guarantee catch-up after a long outage.

### Freshness and reliability

Trawle's collection pipeline runs daily. Running this Actor retrieves the existing index; it does
not trigger fresh employer crawls. `retrievedAt` is not an employer check, `firstSeenAt` is not
necessarily the employer's publication date, and `lastSeenAt` shows the last observed open state.
Jobs can close between checks. The Actor does not certify that an employer is actively hiring.

Requests are serial, paced and retried for transient failures. An upstream failure or invalid
response fails the run rather than presenting an incomplete search as complete. Zero matching
jobs is a valid successful result; try a broader keyword or fewer filters.

### Pricing

**No additional Actor fee.** You pay Apify's standard platform usage for compute and storage under
your Apify plan. The Actor uses 256 MB of memory, no browser and no proxies. There is no Trawle API
key or paid Trawle plan to configure.

### Integration and attribution

Run through the Apify Console, REST API, JavaScript/Python clients or Apify's MCP integration.
Use the listing's **API** tab for generated examples. Outputs can be connected to other workflows
through Apify datasets and webhooks. Please retain the original employer link and credit
**[Data by Trawle](https://trawle.com)** when publishing results.

For direct API access and RSS feeds, visit [developers.trawle.com](https://developers.trawle.com).
For bugs or feature requests, open an issue on this Actor's **Issues** tab with the input and run URL.

# Actor input Schema

## `keyword` (type: `string`):

Optional title, department or company-name search, for example software or data analyst. Matches word prefixes, not full descriptions or every skill mentioned in a job.

## `location` (type: `string`):

Optional exact place name, such as Denmark, Germany or Copenhagen. Exact countries/regions take precedence over same-named cities. Other ambiguous names fail with candidate IDs; use placeIds instead. Confirm remote eligibility on the employer's page.

## `workplace` (type: `string`):

Remote, hybrid, onsite, or any workplace. Remote does not mean worldwide.

## `seniority` (type: `string`):

Only jobs classified at this level; unknown levels do not match a specific filter.

## `employment` (type: `string`):

Only jobs classified with this employment type; unknown values do not match a specific filter.

## `postedWithin` (type: `string`):

Filter by when Trawle first saw a listing, not the employer's publication date. Cannot be combined with postedSince.

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

Maximum unique job IDs exported in this run (1–500). The Actor may return fewer if fewer jobs match. See OUTPUT for pagination and completeness.

## `placeIds` (type: `array`):

Optional array of 1–20 GeoNames integer IDs instead of location. Denmark is 2623032; worldwide is -1. Resolve IDs at https://trawle.com/v1/places?q=Denmark. Multiple places are ORed.

## `postedSince` (type: `string`):

Optional ISO timestamp, e.g. 2026-10-31T00:00:00.000Z. For incremental searches, use nextSince from the last fully exhausted run. Keep this unchanged while paginating; deduplicate across runs by id.

## `cursor` (type: `string`):

Optional nextCursor from OUTPUT. Keep all filters and postedSince identical to the previous run. Finish pagination before advancing postedSince.

## `windowStartedAt` (type: `string`):

Set automatically in OUTPUT.continuationInput. Preserve it together with cursor so the final page can return a safe nextSince. Leave empty for a new search.

## Actor input object example

```json
{
  "workplace": "remote",
  "seniority": "any",
  "employment": "any",
  "postedWithin": "any",
  "maxItems": 20
}
```

# Actor output Schema

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

Structured matching job summaries with employer links, salaries when available, Trawle URLs and freshness dates.

## `search` (type: `string`):

Result count, capped match count, resolved query, nextCursor, hasMore and nextSince (only set after exhausting the search).

# 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 = {
    "workplace": "remote",
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("trawle/trawle-job-search").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 = {
    "workplace": "remote",
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("trawle/trawle-job-search").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 '{
  "workplace": "remote",
  "maxItems": 20
}' |
apify call trawle/trawle-job-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,trawle/trawle-job-search"
        }
    }
}
```

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/IipAzE5wFtbgkQHd1/builds/KFogI0wF2imZRrNkd/openapi.json
