Bulk Job Listings Export With a Cost You Can Budget
Pull Google Jobs openings in bulk and hand back a file. The Actor writes flat rows, so CSV, Excel, Sheets, and JSON exports come out clean, and because billing is per result the invoice equals the row count.
When to use this skill
- The user wants to export Google Jobs listings to CSV, Excel, Google Sheets, or JSON.
- They are seeding or backfilling a job board, an ATS, a warehouse table, or a research dataset.
- They need many roles or many cities in one deliverable file.
- They need to quote a data-pull cost up front, before anyone approves the run.
Not for: a live low-latency search endpoint inside an app, salary analytics (there is no numeric salary field), or LinkedIn-only listings (use the LinkedIn Jobs API).
Why per-result billing matters for bulk work
Bulk exports are where page-based pricing gets unpredictable, because a page that returns three listings costs the same as a page that returns ten. Here you pay for delivered rows. A 500-row export costs 500 result charges whether Google served them across 50 tidy pages or 80 ragged ones, so the estimate you give before the run is the number on the invoice after it.
What lands in the file (one flat row per listing)
Verified on a live run:
title, job_title (same string as title), company_name, location, via (hosting platform), description (full posting text), job_highlights (array of {title, items} blocks such as Qualifications, Responsibilities, Benefits), extensions (raw tags such as "22 hours ago", "Full-time"), detected_extensions (posted_at and schedule_type on every row, plus work_from_home and qualifications when present), apply_options (array of {title, link} direct apply links), source_link, share_link, job_id, and the echoed run context query, country, language, google_domain, search_timestamp, total_jobs_found, pages_processed.
The echoed context is what makes multi-query batches work. Concatenate 12 runs into one table and each row still says which query and market produced it.
For CSV and Excel, the nested columns (job_highlights, apply_options, extensions, detected_extensions) flatten into bracketed sub-columns. Pick a dataset view or select fields when you want a narrow, human-readable sheet. See references/gotchas.md.
Prerequisites
The Actor
Run it with the Apify CLI
One export batch, staffing agencies and aggregators excluded:
apify actors call "johnvc/google-jobs-scraper---pay-per-result" -i '{"query":"registered nurse","location":"Dallas, TX","country":"us","language":"en","num_results":200,"via_filter_list":["ZipRecruiter","Indeed"]}' \
--json \
--user-agent apify-awesome-skills/apify-bulk-job-listings-export \
2>/dev/null
Pull the dataset down as JSON for a database load:
apify datasets get-items DATASET_ID --format json --user-agent apify-awesome-skills/apify-bulk-job-listings-export 2>/dev/null > job_listings.json
For a spreadsheet handoff, ask the dataset export endpoint for CSV directly:
curl -sG "https://api.apify.com/v2/datasets/DATASET_ID/items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
--data-urlencode "format=csv" \
> job_listings.csv
Swap format=csv for format=xlsx when the user wants Excel.
Every call carries the three flags this repo expects: --json (or --format json), --user-agent apify-awesome-skills/apify-bulk-job-listings-export, and 2>/dev/null.
Run it from Claude or another AI agent (MCP)
The Actor is MCP-ready. Add the hosted server URL:
https://mcp.apify.com/?tools=actors,docs,johnvc/google-jobs-scraper---pay-per-result
Then ask, for example: "Export 300 medical assistant openings across Phoenix, Tucson, and Mesa to one CSV, and skip listings from staffing agencies." MCP setup docs: https://docs.apify.com/platform/integrations/mcp
Workflow
- Scope the deliverable with the user. Which roles, which markets, how many rows per combination, and what file format at the end.
- Turn that into one input per query and market pair.
query plus location plus country plus language. Send country and language explicitly, since Google Jobs returns nothing when neither is supplied.
- Price the batch before running. Total rows times the live per-result price. Present the number and get approval when it is large. See
references/gotchas.md.
- Trim the file and the bill together.
company_filter_list and via_filter_list exclude rows during collection, so they never reach the dataset and never get charged.
- Run the batch. Keep runs separate per query and market so a failure costs you one slice instead of the whole export.
- Export and merge. Pull each dataset as CSV or JSON, concatenate, then dedupe on
job_id. The same posting can appear in overlapping city searches.
- Deliver. Hand over the file, or load it into the job board, warehouse, or ATS. For a recurring refresh, wrap the same inputs in an Apify Schedule.
Inputs
query (string, required): role, skill, or company
location (string): city, state, or country
country (enum: None, us, ca, uk, de, fr, au, jp, in, br, mx) and language (enum, 100 plus codes)
google_domain (enum, default google.com)
num_results (integer 10 to 1000, default 100): rows per run, and the cost lever
max_pagination (integer 0 to 100, default 0 = unlimited)
company_filter or company_filter_list: exclude employers
company_filter_regex (boolean, default false): treat company filters as regular expressions
via_filter or via_filter_list: exclude source platforms
include_lrad (boolean) plus lrad_value (string, miles): radius search
output_file (string): custom filename for the saved results file
Cost
Billing is per result in the default dataset, so a bulk export costs rows times the per-result price. A 1,000-row export is 1,000 charges. Multi-query batches add up across runs, so total the whole batch before you start. Live prices are in references/gotchas.md.
Honest limits
- Hard ceiling of 1,000 results per run. Larger exports mean several runs, split by query or market.
- No numeric salary field and no experience-level field. Compensation sometimes sits in free text inside a
job_highlights Benefits block.
posted_at is a relative string such as "22 hours ago", so convert it to timestamps after export if the file needs real dates.
pages_processed came back as 0 on the verified run. Do not put it in a report; count rows.
- Overlapping city or radius searches return duplicate postings. Dedupe on
job_id before delivering.
- This is a batch export, not a streaming feed. Freshness is the age of your last run.
Troubleshooting
- Empty export: confirm
country and language were sent, then broaden the query or widen the location.
- Row count below
num_results: expected, Google had fewer listings. You were billed only for delivered rows.
- CSV columns look exploded: the nested fields expand into sub-columns. Select a narrower field set or use a dataset view.
- A single row describing insufficient funds: account balance was below the estimated row count. Add funds or lower
num_results.
See references/gotchas.md for cost guardrails, export formats, and error recovery, and references/actor-index.md for the Actor routing table.
Related job-data Actors
1---2name: apify-bulk-job-listings-export3description: Export Google Jobs listings to CSV, Excel, Sheets, or JSON in bulk with the Apify Google Jobs Scraper API Pay Per Result Actor (johnvc/google-jobs-scraper---pay-per-result). Output is already flat, one row per opening, so a dataset export drops straight into a spreadsheet or a database load with no reshaping. Covers multi-query batches, exclusion filters that shrink the file and the bill together, and job_id dedupe across runs. Use when the user wants to export google jobs to csv, download job listings as a spreadsheet, build or backfill a job board, load openings into a warehouse or ATS, or bulk export job postings for many roles and cities at a known cost. Pay-per-result billing, MCP-ready for Claude and other AI agents.4license: MIT5---67# Bulk Job Listings Export With a Cost You Can Budget89Pull Google Jobs openings in bulk and hand back a file. The Actor writes flat rows, so CSV, Excel, Sheets, and JSON exports come out clean, and because billing is per result the invoice equals the row count.1011## When to use this skill1213- The user wants to export Google Jobs listings to CSV, Excel, Google Sheets, or JSON.14- They are seeding or backfilling a job board, an ATS, a warehouse table, or a research dataset.15- They need many roles or many cities in one deliverable file.16- They need to quote a data-pull cost up front, before anyone approves the run.1718Not for: a live low-latency search endpoint inside an app, salary analytics (there is no numeric salary field), or LinkedIn-only listings (use the LinkedIn Jobs API).1920## Why per-result billing matters for bulk work2122Bulk exports are where page-based pricing gets unpredictable, because a page that returns three listings costs the same as a page that returns ten. Here you pay for delivered rows. A 500-row export costs 500 result charges whether Google served them across 50 tidy pages or 80 ragged ones, so the estimate you give before the run is the number on the invoice after it.2324## What lands in the file (one flat row per listing)2526Verified on a live run:2728`title`, `job_title` (same string as `title`), `company_name`, `location`, `via` (hosting platform), `description` (full posting text), `job_highlights` (array of `{title, items}` blocks such as Qualifications, Responsibilities, Benefits), `extensions` (raw tags such as "22 hours ago", "Full-time"), `detected_extensions` (`posted_at` and `schedule_type` on every row, plus `work_from_home` and `qualifications` when present), `apply_options` (array of `{title, link}` direct apply links), `source_link`, `share_link`, `job_id`, and the echoed run context `query`, `country`, `language`, `google_domain`, `search_timestamp`, `total_jobs_found`, `pages_processed`.2930The echoed context is what makes multi-query batches work. Concatenate 12 runs into one table and each row still says which query and market produced it.3132For CSV and Excel, the nested columns (`job_highlights`, `apply_options`, `extensions`, `detected_extensions`) flatten into bracketed sub-columns. Pick a dataset view or select fields when you want a narrow, human-readable sheet. See `references/gotchas.md`.3334## Prerequisites3536- Apify account (sign up at https://apify.com?fpr=9n7kx3&fp_sid=skillrepo).37- Authentication via `apify login`, or an `APIFY_TOKEN` environment variable (Apify Console, Settings, Integrations).3839## The Actor4041- Store page: https://apify.com/johnvc/google-jobs-scraper---pay-per-result?fpr=9n7kx3&fp_sid=skillrepo42- Actor ID: `johnvc/google-jobs-scraper---pay-per-result`43- Pricing: pay per result delivered to the dataset (see `references/gotchas.md`).4445## Run it with the Apify CLI4647One export batch, staffing agencies and aggregators excluded:4849```bash50apify actors call "johnvc/google-jobs-scraper---pay-per-result" -i '{"query":"registered nurse","location":"Dallas, TX","country":"us","language":"en","num_results":200,"via_filter_list":["ZipRecruiter","Indeed"]}' \51 --json \52 --user-agent apify-awesome-skills/apify-bulk-job-listings-export \53 2>/dev/null54```5556Pull the dataset down as JSON for a database load:5758```bash59apify datasets get-items DATASET_ID --format json --user-agent apify-awesome-skills/apify-bulk-job-listings-export 2>/dev/null > job_listings.json60```6162For a spreadsheet handoff, ask the dataset export endpoint for CSV directly:6364```bash65curl -sG "https://api.apify.com/v2/datasets/DATASET_ID/items" \66 -H "Authorization: Bearer $APIFY_TOKEN" \67 --data-urlencode "format=csv" \68 > job_listings.csv69```7071Swap `format=csv` for `format=xlsx` when the user wants Excel.7273Every call carries the three flags this repo expects: `--json` (or `--format json`), `--user-agent apify-awesome-skills/apify-bulk-job-listings-export`, and `2>/dev/null`.7475## Run it from Claude or another AI agent (MCP)7677The Actor is MCP-ready. Add the hosted server URL:7879`https://mcp.apify.com/?tools=actors,docs,johnvc/google-jobs-scraper---pay-per-result`8081Then ask, for example: "Export 300 medical assistant openings across Phoenix, Tucson, and Mesa to one CSV, and skip listings from staffing agencies." MCP setup docs: https://docs.apify.com/platform/integrations/mcp8283## Workflow84851. Scope the deliverable with the user. Which roles, which markets, how many rows per combination, and what file format at the end.862. Turn that into one input per query and market pair. `query` plus `location` plus `country` plus `language`. Send `country` and `language` explicitly, since Google Jobs returns nothing when neither is supplied.873. Price the batch before running. Total rows times the live per-result price. Present the number and get approval when it is large. See `references/gotchas.md`.884. Trim the file and the bill together. `company_filter_list` and `via_filter_list` exclude rows during collection, so they never reach the dataset and never get charged.895. Run the batch. Keep runs separate per query and market so a failure costs you one slice instead of the whole export.906. Export and merge. Pull each dataset as CSV or JSON, concatenate, then dedupe on `job_id`. The same posting can appear in overlapping city searches.917. Deliver. Hand over the file, or load it into the job board, warehouse, or ATS. For a recurring refresh, wrap the same inputs in an Apify Schedule.9293## Inputs9495- `query` (string, required): role, skill, or company96- `location` (string): city, state, or country97- `country` (enum: None, us, ca, uk, de, fr, au, jp, in, br, mx) and `language` (enum, 100 plus codes)98- `google_domain` (enum, default `google.com`)99- `num_results` (integer 10 to 1000, default 100): rows per run, and the cost lever100- `max_pagination` (integer 0 to 100, default 0 = unlimited)101- `company_filter` or `company_filter_list`: exclude employers102- `company_filter_regex` (boolean, default false): treat company filters as regular expressions103- `via_filter` or `via_filter_list`: exclude source platforms104- `include_lrad` (boolean) plus `lrad_value` (string, miles): radius search105- `output_file` (string): custom filename for the saved results file106107## Cost108109Billing is per result in the default dataset, so a bulk export costs rows times the per-result price. A 1,000-row export is 1,000 charges. Multi-query batches add up across runs, so total the whole batch before you start. Live prices are in `references/gotchas.md`.110111## Honest limits112113- Hard ceiling of 1,000 results per run. Larger exports mean several runs, split by query or market.114- No numeric salary field and no experience-level field. Compensation sometimes sits in free text inside a `job_highlights` Benefits block.115- `posted_at` is a relative string such as "22 hours ago", so convert it to timestamps after export if the file needs real dates.116- `pages_processed` came back as 0 on the verified run. Do not put it in a report; count rows.117- Overlapping city or radius searches return duplicate postings. Dedupe on `job_id` before delivering.118- This is a batch export, not a streaming feed. Freshness is the age of your last run.119120## Troubleshooting121122- Empty export: confirm `country` and `language` were sent, then broaden the query or widen the location.123- Row count below `num_results`: expected, Google had fewer listings. You were billed only for delivered rows.124- CSV columns look exploded: the nested fields expand into sub-columns. Select a narrower field set or use a dataset view.125- A single row describing insufficient funds: account balance was below the estimated row count. Add funds or lower `num_results`.126127See `references/gotchas.md` for cost guardrails, export formats, and error recovery, and `references/actor-index.md` for the Actor routing table.128129## Related job-data Actors130131- Google Jobs Scraper, pay per page edition: https://apify.com/johnvc/Google-Jobs-Scraper?fpr=9n7kx3&fp_sid=skillrepo132- LinkedIn Jobs API: https://apify.com/johnvc/linkedin-jobs-api?fpr=9n7kx3&fp_sid=skillrepo133- Glassdoor Reviews API: https://apify.com/johnvc/glassdoor-reviews-api?fpr=9n7kx3&fp_sid=skillrepo