# Gupy Jobs Scraper - Jobs, Companies, Locations & Deadlines (`abotapi/gupy-io-scraper`) Actor

Scrape public jobs from Gupy.io by keyword, state, city, workplace type, job type, company, PWD, and feedback badge. Extract rich job details, company information, locations, application deadlines, and badge data.

- **URL**: https://apify.com/abotapi/gupy-io-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Gupy.io Jobs Scraper

Scrape public job listings from Gupy.io. The actor supports keyword search, Brazilian state and city filters, workplace model, job type, exact Gupy career page name, PWD-only listings, feedback-badge companies, URL mode, optional detail enrichment, and newest/oldest/company sorting.

### Pricing

Pay per event:

| Event | Price |
| --- | ---: |
| Actor start | $0.08 |
| Job result | $0.002 |
| Detail enrichment | $0.0008 |

Base result pricing is $2.00 per 1,000 saved jobs plus the run-start event. Detail enrichment adds $0.80 per 1,000 enriched jobs when enabled.

### Input

Use Search mode for filters:

```json
{
  "searchTerm": "developer",
  "state": "São Paulo",
  "workplaceTypes": ["remote"],
  "sortBy": "newest",
  "fetchDetails": true,
  "maxItems": 20
}
```

Use URL mode for portal search URLs or company job URLs:

```json
{
  "mode": "url",
  "urls": [
    "https://portal.gupy.io/job-search/term=developer",
    "https://sejaesm.gupy.io/job/eyJqb2JJZCI6MTE1MjA0MzksInNvdXJjZSI6Imd1cHlfcG9ydGFsIn0=?jobBoardSource=gupy_portal"
  ],
  "maxItems": 5
}
```

`maxItems` is the only finite default cap and means the maximum matching rows to save. Fewer rows may be returned when the selected source, keyword, and filters match fewer live records. `maxPages` has no page limit by default; leave it empty (or 0) to walk every result page, or set it to stop after a fixed number of API pages.

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| searchTerm | string | (empty) | Job title, skill, or keyword. Leave empty to scrape all jobs matching the other filters. |
| state | string | (empty) | Brazilian state filter. Leave empty for all states. |
| cities | array | \[] | Exact Gupy city names. Leave empty for all cities. |
| workplaceTypes | array | \[] | Remote, hybrid, or on-site jobs. Leave empty for all workplace types. |
| jobTypes | array | \[] | Gupy vacancy type values. Leave empty for all job types. |
| companies | array | \[] | Exact Gupy career page names. Leave empty for all companies. |
| pwdOnly | boolean | `false` | Return only jobs marked for people with disabilities. |
| friendlyBadgeOnly | boolean | `false` | Return only jobs from companies with Gupy feedback badge. |
| fetchDetails | boolean | `false` | Fetch the full per-job record before saving each result. Adds the detail-enrichment charge per enriched job saved. |
| sortBy | string | `newest` | Sort saved jobs after collection (newest, oldest, or company A-Z). |
| maxItems | integer | 20 | Maximum matching rows to save. Fewer rows may be returned when the selected source and filters match fewer live records. |
| maxPages | integer | 0 | Leave empty to walk every result page. Set 0 for unlimited; the run stops at Max items. A positive number caps the walk at that many pages. |
| pageSize | integer | 100 | API page size. Larger pages are faster; smaller pages are useful for debugging. |
| mcpConnectors | array | \[] | Optionally send the scraped jobs into the apps you already use, via MCP connectors (Notion, Linear, Airtable, Apify). The connector receives a condensed summary per job; the full record stays in the dataset. Leave empty to skip. |
| notionParentPageUrl | string | (empty) | URL (or id) of the Notion page under which job pages are created. Required to enable Notion export; ignored by other connectors. |
| maxNotifyListings | integer | 50 | Cap on jobs written to each connector per run. Does not affect the dataset. |

### 🔁 Resume & recurring updates

| Parameter | Type | Default | Description |
|---|---|---|---|
| resumeFromRunId | string | (empty) | A previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Jobs already collected there (matched by job id) are skipped, so this run only saves the new ones. |
| incrementalMode | boolean | `false` | Turn on for scheduled/recurring monitoring of the same search. The first run returns everything as `NEW`; later runs return only `NEW`/`UPDATED`/`REAPPEARED` (and, optionally, `UNCHANGED`/`EXPIRED`) jobs. |
| stateKey | string | (empty) | Optional. Name an incremental-mode monitoring campaign explicitly, or keep two schedules with identical filters from sharing one baseline. Leave empty to derive a key automatically from the mode, keyword, state, cities, workplace/job types, companies, PWD/badge flags, URLs, and Fetch details. |
| emitUnchanged | boolean | `false` | Incremental mode only. Also return (and bill) jobs that have not changed since the last run, marked `UNCHANGED`. |
| emitExpired | boolean | `false` | Incremental mode only. Also return (and bill) jobs present in a previous run but no longer found, marked `EXPIRED`. Only produced when a run fully scans the tracked search (not capped by Max items, not `maxPages`-truncated, not a resume). |

#### Resuming a full-catalogue walk

To pull a large catalogue across several separate runs without re-saving jobs you already have, paste a prior run ID or dataset ID into **Resume from a previous run**. The new run reads that dataset's job ids, skips any job already collected there, and appends only new jobs. An invalid or unreadable run/dataset id fails the run immediately, before any scraping starts.

#### Incremental mode (recurring monitoring)

`resumeFromRunId` and `incrementalMode` solve two different problems: Resume continues **one** specific interrupted run; Incremental mode is for scheduling this **same search** again and again (e.g. daily) and getting only what changed, without pasting any run/dataset id.

Turn on `incrementalMode` and each run classifies every job against the state from the last run on the same search (kept internally, keyed by the search filters, or by your own `stateKey`) and adds a `changeType` field:

- `NEW` — not seen before.
- `UPDATED` — seen before, and at least one field changed (`changedFields` lists which).
- `UNCHANGED` — seen before, nothing changed. Suppressed (not returned or billed) unless `emitUnchanged` is on.
- `REAPPEARED` — was previously marked `EXPIRED`, now found again.
- `EXPIRED` — was previously seen, not found in a run that fully scanned the tracked search. Only emitted when `emitExpired` is on.

Every job also carries `firstSeenAt` / `lastSeenAt` in incremental mode. The detail-enrichment surcharge is billed only for jobs that actually reach the dataset — a suppressed `UNCHANGED` job's detail page may still be fetched (needed to compute the fingerprint) but is never charged.

### Send results into your apps (MCP connectors)

Optionally pipe results into Notion, Linear, Airtable, or any Apify-supported MCP server. Authorize a connector once under **Apify → Settings → Integrations**, then select it in the **Export to your apps** input. Each connector receives a condensed, human-readable summary per job (title + key fields); the full record always stays in the dataset. For Notion, also set **Notion parent page**. Use **Max jobs to export per connector** to cap how many jobs are written per run. This is a fail-safe side channel — connector errors never affect the scrape or the dataset.

### Output

Each result includes job identifiers, title, URL, HTML and plain-text description, location, job type, workplace type, remote flag, publication and application deadline dates, company/career page metadata, badge/PWD flags, source fields, and a scrape timestamp. With **Incremental mode** on, each result also carries `changeType` (`NEW`/`UPDATED`/`UNCHANGED`/`REAPPEARED`/`EXPIRED`), `changedFields` (which fields changed, for `UPDATED`), `firstSeenAt`, and `lastSeenAt` — see [Incremental mode](#incremental-mode-recurring-monitoring) above.

### Proxy Guidance

Works on every plan, including Free. Apify Proxy is recommended for reliability and is the default; if you see repeated source errors, enable it.

### Notes

The actor extracts public job data. Users are responsible for compliance with applicable laws, platform terms, and privacy obligations.

# Actor input Schema

## `mode` (type: `string`):

Search mode uses the filters below. URL mode accepts portal search URLs and Gupy company job URLs.

## `searchTerm` (type: `string`):

Job title, skill, or keyword. Leave empty to scrape all jobs matching the other filters.

## `state` (type: `string`):

Brazilian state filter. Leave empty for all states.

## `cities` (type: `array`):

Exact Gupy city names. Leave empty for all cities.

## `workplaceTypes` (type: `array`):

Remote, hybrid, or on-site jobs. Leave empty for all workplace types.

## `jobTypes` (type: `array`):

Gupy vacancy type values. Leave empty for all job types.

## `companies` (type: `array`):

Exact Gupy career page names, for example '#sejaveriter' or 'PagBank'. Leave empty for all companies.

## `pwdOnly` (type: `boolean`):

Return only jobs marked for people with disabilities.

## `friendlyBadgeOnly` (type: `boolean`):

Return only jobs from companies with Gupy feedback badge.

## `urls` (type: `array`):

Portal search URLs or Gupy company job URLs. Used only when Mode is URLs.

## `fetchDetails` (type: `boolean`):

Fetch the full per-job record before saving each result. Applies to Search mode and to portal search URLs. Direct Gupy job URLs are always fetched in full, so this toggle does not affect them. Adds the detail-enrichment charge per enriched job that is actually saved — a job suppressed by Incremental mode (UNCHANGED, Emit unchanged off) is never charged even though its detail page was fetched. Also part of the Incremental mode baseline key: turning it on or off starts a separate tracked baseline.

## `sortBy` (type: `string`):

Sort saved jobs after collection. Applies in both Search and URL mode.

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

Maximum matching rows to save. Fewer rows may be returned when the selected source, keyword, and filters match fewer live records.

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

Leave empty to walk every result page. Set 0 for unlimited; the run stops at Max items. A positive number caps the walk at that many pages.

## `pageSize` (type: `integer`):

API page size. Larger pages are faster; smaller pages are useful for debugging.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Jobs already collected there (matched by job id) are skipped, so this run only saves the new ones. Leave empty for a normal fresh run. For recurring monitoring of the same search instead, use Incremental & dedup mode below.

## `incrementalMode` (type: `boolean`):

Remember this search's results between scheduled runs and classify each job as NEW, UPDATED, UNCHANGED, REAPPEARED, or EXPIRED (changeType field). Unlike Resume from a previous run (which continues one specific run by pasted id), this actor tracks its own baseline automatically, keyed by the search filters below (or State key). Off by default — every run scrapes and saves everything it finds.

## `stateKey` (type: `string`):

Only used when Incremental & dedup mode is on. Leave empty to key the baseline automatically from the search filters (mode, keyword, state, cities, workplace/job types, companies, PWD/badge flags, URLs, Fetch details). Set your own text to track a named monitoring campaign explicitly, or to keep two schedules with identical filters from sharing one baseline.

## `emitUnchanged` (type: `boolean`):

Only used when Incremental & dedup mode is on. When off (default), a job identical to the last tracked run is skipped — not saved, not billed. When on, UNCHANGED rows are saved (and billed) too, alongside NEW/UPDATED/REAPPEARED ones.

## `emitExpired` (type: `boolean`):

Only used when Incremental & dedup mode is on. When on, a job tracked by a previous run but no longer found after a complete scan of this search is saved once with changeType EXPIRED (and billed as a normal result row). Only fires on a run that reached the natural end of the walk — Max items/Max pages capping the run, or Resume from a previous run being set, skips EXPIRED detection for that run and keeps the previous state as-is.

## `mcpConnectors` (type: `array`):

Optionally send the scraped jobs 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 job (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` (type: `string`):

URL (or id) of the Notion page under which job pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on jobs written to each connector per run. Does not affect the dataset.

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

Works on every plan, including Free. Apify Proxy is recommended for reliability and is the default; if you see repeated source errors, enable it.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerm": "developer",
  "state": "",
  "cities": [],
  "workplaceTypes": [],
  "jobTypes": [],
  "companies": [],
  "pwdOnly": false,
  "friendlyBadgeOnly": false,
  "urls": [],
  "fetchDetails": false,
  "sortBy": "newest",
  "maxItems": 20,
  "maxPages": 0,
  "pageSize": 100,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": []
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# 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 = {
    "searchTerm": "developer",
    "sortBy": "newest",
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": []
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/gupy-io-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 = {
    "searchTerm": "developer",
    "sortBy": "newest",
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": [],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/gupy-io-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 '{
  "searchTerm": "developer",
  "sortBy": "newest",
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": []
  }
}' |
apify call abotapi/gupy-io-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/gupy-io-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/G32z7UUaT9p3lAVY7/builds/JF7hN3foLdlwbPlzr/openapi.json
