104 Taiwan Jobs avatar

104 Taiwan Jobs

Pricing

$0.63 / 1,000 results

Go to Apify Store
104 Taiwan Jobs

104 Taiwan Jobs

Pricing

$0.63 / 1,000 results

Rating

0.0

(0)

Developer

Stone Jade

Stone Jade

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

Categories

Share

Collect public job listings from 104.com.tw for recruiting research, salary analysis and hiring-market monitoring. Search multiple keywords, filter by area and job category, and export unique jobs to an Apify dataset. Optional detail requests add qualifications, benefits and work conditions. Recruiter contacts are omitted.

The Actor is job104-taiwan-jobs, uses Node.js ESM, and defaults to 128 MB.

Example results

These values come from the recorded research fixture list_ps30.json; they are examples, not a promise that a job is still available.

FieldExample
jobCode89sn1
jobNo13893949
jobName<韌體工程師> Firmware Engineer 物聯網無線模組產業
custName勁達國際電子股份有限公司
jobAddrNoDesc新北市中和區
salaryLow, salaryHigh0, 0 (negotiable in this listing)
urlhttps://www.104.com.tw/job/89sn1

Results keep useful native 104 list field names: jobName, custName, custNo, appearDate, jobCat, industry, address, coordinates, salary bounds, description, education codes and remote-work type. Publication dates use ISO YYYY-MM-DD in both list and detail fields. Uncapped salary maxima (9999999) become null, with salaryMaxUnbounded: true. salaryType preserves the source type code (list s10); salaryUnit is piece, hour, day, month, year, or null for negotiable/unknown types. Zero salary bounds remain source values; no salary amounts are estimated.

With fetchDetails=true, results also contain header, jobDetail, condition, welfare and company objects, including salary bounds, work location, requirements, benefits and job description. Recruiter contact fields and viewer state are omitted. Email, Taiwan phone patterns and labeled recruiter names in free text are masked, including when the legacy includeContact option is true. Masking applies only to description, other-condition text, and welfare text. Codes, IDs, URLs, coordinates and other structured fields retain their source values. Free-text masking is heuristic and cannot recognize every possible personal detail. The full output schema is in .actor/dataset_schema.json.

Inputs

InputDefaultMeaning
keywords[]Separate OR searches, e.g. ["python", "資料分析"]; empty searches all jobs.
areaCodes[]Ten-digit strings, e.g. ["6001001000"] for Taipei City; empty is unrestricted.
jobCategoryCodes[]Ten-digit strings, e.g. ["2007000000"] for IT/software; empty is unrestricted.
maxItems50Positive integer; global maximum unique saved jobs across all searches.
fetchDetailsfalseOne extra detail request per new unique job.
detailConcurrency3Maximum simultaneous details; drops to 1 for the rest of the run after a 429.
includeContactfalseDeprecated compatibility option; contact output remains disabled.
useProxy"none""none" or "datacenter"; datacenter uses Actor.createProxyConfiguration({ groups: [] }).
{
"keywords": ["python"],
"areaCodes": ["6001001000"],
"jobCategoryCodes": ["2007000000"],
"maxItems": 50,
"fetchDetails": false,
"includeContact": false,
"useProxy": "none"
}

Public code trees: areas and job categories. Copies recorded with the research samples are bundled in data/. These support automatic query splitting without downloading code lists during each run. Unknown ten-digit codes can be submitted but have no known descendants for splitting.

Pagination and limits

Every list request uses pagesize=30. 104 may return two extra promoted jobs; all returned rows are considered and deduplicated globally by jobNo. The Actor stops at the response's lastPage, including clamped final pages.

104 caps many searches at about 3,000 results. If more jobs are needed after the first page, a query with total > lastPage * 30 is split into area × category slices. Supplied code arrays are separated, and capped single branches are recursively refined using the bundled trees. An unrestricted capped query starts with the eight area roots × eighteen category roots. Capped children are split into sub-areas and sub-categories until pageable or no descendants remain. The global item limit still applies. Parent and slice overlaps are deduplicated.

If a capped leaf or unknown code cannot be refined, the Actor reads its available pages and records OUTPUT.cappedQueries plus a warning. It does not guarantee exhaustive coverage of these slices. Jobs may change while a run is in progress; the site does not provide a consistent snapshot.

List requests are serial. Details use up to detailConcurrency workers, with one second between all request starts, including retries. After a 429, already running details drain and subsequent jobs run one at a time. Only the remaining gap is waited: a response taking over a second adds no sleep. Robots crawl delays, retry backoff and Retry-After add no sleeps. HTTP 403/429 and network failures retry at most twice at the same one-second start gap; each request has a 30-second timeout. Other HTTP errors, redirects, malformed payloads or unavailable details fail the run. A run that saves zero jobs fails, including genuinely empty searches, except when collection stops at the Actor deadline. Already saved jobs remain available if a later request fails.

When ACTOR_TIMEOUT_AT or Actor.getEnv().timeoutAt provides a deadline, collection stops 15 seconds before it. Pending target requests are aborted; saved dataset items remain available and OUTPUT is written before exit.

The final Detail and memory summary logs http429Count (429 responses only), averageDetailMs (per-job detail elapsed time including waits and retries), and memoryPeakRssBytes. RSS is sampled with process.memoryUsage().rss every five seconds and at run start/end. These metrics are also stored in OUTPUT.

OUTPUT records item/request counts, list pages, detail requests, deduplicated rows, retries, rate limits, splits, unrefinable caps, errors, robots disallow hits and whether the SDK billing limit or timeout margin stopped collection. The final Query slice summary log and OUTPUT.slices include each slice's filters, reported total/lastPage, fetched pages, rows before dedupe, new global job numbers after dedupe, saved items, duplicates and completion status. totalBeforeDedupe and totalAfterDedupe count all fetched rows and unique jobNo values across the run. They include any unsaved remainder of a page when maxItems, billing or the deadline stops saving; items counts saved rows. OUTPUT.listRequestStats records HTTP attempts, response bytes, cumulative and maximum HTTP elapsed time, gap waits and status counts. Debug logs show each list attempt's status, bytes, elapsedMs and waitMs; a separate page log shows save time and total page time. The final info summary includes averages, listSaveMs (saving, including optional details) and listPageElapsedMs. Enable debug logging with APIFY_LOG_LEVEL=DEBUG to diagnose slow list pages.

Billing

Each list page's new validated results are submitted in one Actor.pushData(arrayOfItems, 'job') call, using the SDK's combined dataset/event operation with per-item charging. The batch is deduplicated within the page and across the run, and capped at the remaining maxItems allowance. Duplicates, empty results and jobs whose requested details fail do not trigger a job event. Collection stops when the SDK trims the batch or reports a billing limit; only saved rows count toward items. No separate charge call is used.

Price: $0.000625 per job — $0.625 per 1,000 jobs, with no run-start fee. You pay only for jobs actually saved (duplicates, empty results and jobs whose requested details fail are not charged, as described above), so 1,000 saved jobs cost you $0.625 in total. For comparison, the main 104 scraper on Apify (corvuslab/taiwan104-jobs-scraper) charges $0.00125 per job plus a $0.005 run-start fee, so the same 1,000 jobs cost $1.255 there — about twice as much.

Run locally

npm ci
mkdir -p storage/key_value_stores/default
# Save the JSON example above as storage/key_value_stores/default/INPUT.json.
npm start
npm test

Local results are under storage/datasets/default, with counters in the default key-value store's OUTPUT.json. On Apify, the default dataset can be exported as JSON, CSV or Excel. Datacenter proxy use requires appropriate Apify proxy access; direct access is the default. Tests use recorded fixtures and simulated responses, without live requests or actual billing. See TESTING.md.

This Actor collects publicly visible job listings without logging in or using account cookies. It does not access applicants or private member data. Recruiter contact fields are omitted and recognizable contacts in text are masked. You are responsible for having an appropriate legal basis and for complying with applicable privacy laws, including Taiwan's Personal Data Protection Act, and the site's terms. Consult legal advice for your specific use. See Apify's discussion of web scraping legality.

104's robots.txt disallows search paths in the research findings. Disallow rules produce one warning per run and increment OUTPUT.robotsDisallowedHits for each affected endpoint; the Actor continues as configured with the one-second request start gap.

This is an independent tool, with no affiliation, endorsement or sponsorship by 104 Corporation. Data is provided as received for research and analysis; respect third-party rights when using or republishing descriptions.