eJobs.ro Job Listings Scraper
Pricing
from $1.10 / 1,000 ejobs.ro job listings scrapers
eJobs.ro Job Listings Scraper
Extract Romanian job listings from the public eJobs JSON API, including job taxonomy, company and location metadata, and optional full job descriptions.
Pricing
from $1.10 / 1,000 ejobs.ro job listings scrapers
Rating
0.0
(0)
Developer
Vitalii Bondarev
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 hours ago
Last modified
Categories
Share
eJobs.ro Scraper
Scrape public job listings from eJobs Romania.
This actor reads the public api.ejobs.ro JSON API with Python curl_cffi. It does not launch or control a browser, does not use Playwright or Selenium, and does not scrape the Cloudflare-protected eJobs website or its HTML pages. All job and taxonomy data comes from public JSON API responses.
What it collects
The actor can collect listing-level job data such as:
- Job ID, title, URL, and publication dates
- Company name and company identifiers
- City, county, country, and remote/work-model information
- Employment type, career level, and domain/category data
- Salary information when published by the employer
- Listing description data
- Optional full detail data from the public job-detail API endpoint
- Normalized taxonomy values for locations, domains, job types, career levels, and work models
Each job is stored as one dataset record.
Quick start
Use a minimal input to collect jobs matching a search phrase:
{"search": "python developer","maxItems": 50}
To fetch the richer job-detail response for every collected job:
{"search": "python developer","maxItems": 50,"fetchDetails": true}
To search and apply local filters:
{"search": "developer","maxItems": 100,"fetchDetails": true,"filters": {"locations": ["București"],"jobTypes": ["Full time"],"careerLevels": ["Mid-Level"],"remoteOnly": true}}
Main input
| Input | Type | Description |
|---|---|---|
search | string | Search phrase sent to the public eJobs API where supported. Use an empty value to browse available jobs without a text query. |
maxItems | integer | Maximum number of matching job records to emit. This is a record limit, not a page limit. |
startPage | integer | Optional API page from which to begin pagination. Pagination normally starts at page 1. |
maxPages | integer | Optional maximum number of API pages to request. Use this to put an additional bound on a broad search. |
fetchDetails | boolean | When true, request the public detail endpoint for each matched job and enrich the listing record with available detail data. |
filters | object | Optional client-side filters applied to normalized listing data. See Client-side filters. |
proxyConfiguration | object | Optional Apify Proxy configuration. If omitted, the actor uses a Romanian RESIDENTIAL proxy configuration by default. |
The actor accepts normal Apify proxy settings. Supplying a custom proxy configuration overrides the default Romanian residential proxy selection.
Proxy behavior
The default network configuration uses an Apify RESIDENTIAL proxy with Romanian routing.
Romanian residential routing is the default because eJobs is a Romanian job board and API behavior may differ by region. A proxy is still an HTTP transport setting only: the actor calls the JSON API directly and does not attempt to visit or bypass the Cloudflare-protected website.
Client-side filters
The API search response can be broad or incomplete for some combinations of criteria. For predictable results, the actor applies the following filters locally after normalizing each listing.
There are no hidden client-side filters beyond the fields listed below.
| Filter field | Accepted value | Behavior |
|---|---|---|
includeKeywords | string or array of strings | Keeps jobs whose searchable text contains the supplied keyword values. Searchable text includes title, company, location labels, and available description text. |
excludeKeywords | string or array of strings | Rejects jobs whose searchable text contains any excluded keyword. |
includeCompanies | string or array of strings | Keeps jobs whose company name matches one of the supplied company values. |
excludeCompanies | string or array of strings | Rejects jobs whose company name matches an excluded value. |
locations | string or array | Keeps jobs matching one or more normalized location names or location IDs. |
counties | string or array | Keeps jobs matching one or more Romanian county names or IDs when county data is available. |
domains | string or array | Keeps jobs matching one or more normalized job domains/categories or their IDs. |
jobTypes | string or array | Keeps jobs matching one or more normalized employment types, such as full-time, part-time, internship, or contract values exposed by the API. |
careerLevels | string or array | Keeps jobs matching one or more normalized career levels. |
workModels | string or array | Keeps jobs matching one or more normalized work models, such as onsite, hybrid, or remote when supplied by eJobs. |
remoteOnly | boolean | When true, keeps only jobs marked as remote or having a remote work model. |
salaryMin | number | Keeps jobs whose published salary range can satisfy the requested minimum salary. Jobs without usable salary data do not match this filter. |
salaryMax | number | Keeps jobs whose published salary range can satisfy the requested maximum salary. Jobs without usable salary data do not match this filter. |
salaryCurrency | string | Restricts salary matching to the supplied currency code when salary data includes a currency. |
withSalary | boolean | When true, keeps only jobs with published salary data. When false, keeps only jobs without published salary data. |
postedAfter | ISO date or datetime string | Keeps jobs published on or after the supplied value. |
postedBefore | ISO date or datetime string | Keeps jobs published on or before the supplied value. |
Filter matching rules
- Text matching is case-insensitive.
- A list within one filter is treated as an OR condition. For example,
locations: ["București", "Cluj-Napoca"]accepts either location. - Different filter groups are combined with AND. A job must satisfy every supplied filter group.
- Exclusion filters take precedence over inclusion filters.
- Taxonomy filters can use normalized names or IDs when those IDs are available in the API taxonomy.
maxItemsis applied after client-side filtering. The actor continues paginating until it has emitted the requested number of matching jobs or there are no more results.
Example:
{"search": "software engineer","maxItems": 100,"filters": {"includeKeywords": ["python", "backend"],"excludeKeywords": ["senior"],"locations": ["București", "Cluj-Napoca"],"jobTypes": ["Full time"],"careerLevels": ["Mid-Level"],"withSalary": true}}
Taxonomy normalization
eJobs API responses may contain internal IDs, labels, partial objects, or differently shaped values depending on the endpoint. The actor resolves these into consistent taxonomy fields where possible.
Taxonomy is fetched once per run and reused for all listing and detail records in that run. It is not fetched separately for every job.
Representative normalized taxonomy fields include:
| Output field | Description |
|---|---|
locations | Normalized location objects and/or labels associated with the job. |
locationIds | API location identifiers when available. |
cities | City names derived from the job location data. |
counties | County names or identifiers when exposed by the API. |
domains | Normalized job domain/category objects or labels. |
domainIds | API domain/category IDs when available. |
jobTypes | Normalized employment-type labels and IDs. |
careerLevels | Normalized career-level labels and IDs. |
workModels | Normalized work-model values, including remote, hybrid, or onsite where supplied. |
isRemote | Boolean remote indicator derived from the available API data. |
The actor preserves useful listing-level values even when a taxonomy item cannot be resolved. Taxonomy labels should be treated as values supplied by eJobs and may change over time.
Optional detail fetching
Set fetchDetails to true to request the public job-detail API response for each matched listing.
Detail responses can provide richer information than the listing feed, including fuller descriptions, application information, salary details, employer data, or additional job metadata when eJobs exposes them.
The actor deliberately treats a failed detail request as non-fatal:
- The job is first collected from the listing API.
- The actor attempts the individual detail request when
fetchDetailsis enabled. - If that detail request succeeds, available detail values enrich the record.
- If an individual detail request fails, the actor still emits the job using the listing data.
A failed individual detail request therefore does not discard an otherwise valid listing and does not stop the rest of the run.
Description fields
When description content is available, the actor exposes both markup and readable text forms:
| Field | Description |
|---|---|
descriptionHtml | Description markup supplied by the public API. Preserve this field when formatting and links matter. |
descriptionText | Plain-text version of the description for search, analysis, exports, and language processing. |
shortDescription | Shorter listing-level summary when supplied separately by the API. |
If fetchDetails is disabled, description fields may be limited to what is present in the listing response. With detail fetching enabled, the actor uses the richer detail description when it is available.
Pagination
The actor paginates through the public eJobs listing API.
Pagination behavior:
- Pages are requested in ascending order, beginning with
startPageor page 1. - The actor stops after
maxItemsmatching records have been emitted. - If
maxPagesis set, the actor also stops after that many requested pages. - The actor stops when the API has no additional results, returns an empty page, or indicates the end of the result set.
- Client-side filters are evaluated for every listing before it counts toward
maxItems. - Repeated job IDs are de-duplicated where possible so the same job is not intentionally emitted multiple times during one run.
A broad query with restrictive local filters may require more API pages than the number of records ultimately emitted.
Representative output
The exact set of fields depends on the API response and whether detail fetching is enabled. A representative record can contain fields like the following:
{"id": "job-id","title": "Python Developer","url": "https://www.ejobs.ro/user/locuri-de-munca/python-developer/...","companyName": "Example Company","companyId": "company-id","locations": [{"id": "location-id","name": "București"}],"cities": ["București"],"counties": ["București"],"domains": [{"id": "domain-id","name": "IT Software"}],"jobTypes": ["Full time"],"careerLevels": ["Mid-Level"],"workModels": ["Hybrid"],"isRemote": false,"salary": {"min": 8000,"max": 12000,"currency": "RON"},"publishedAt": "2025-01-01T10:00:00Z","expiresAt": "2025-01-31T23:59:59Z","descriptionHtml": "Description markup returned by the API","descriptionText": "Readable job description text","detailFetched": true,"scrapedAt": "2025-01-01T12:00:00Z"}
Common fields include:
| Field | Description |
|---|---|
id | Stable eJobs job identifier when provided. |
title | Job title. |
url | Public job URL when available. |
companyName | Employer name. |
companyId | Employer identifier when available. |
locations, cities, counties | Normalized geographic information. |
domains, jobTypes, careerLevels, workModels | Normalized taxonomy values. |
isRemote | Derived remote-work indicator. |
salary | Published salary range and currency when available. |
publishedAt, expiresAt | Job publication and expiry timestamps when supplied. |
descriptionHtml | API-provided description markup. |
descriptionText | Plain-text description. |
detailFetched | Indicates whether the job-detail request successfully enriched the listing. |
scrapedAt | Timestamp at which the actor processed the record. |
Fields can be absent or null when eJobs does not provide the corresponding information.
Free-plan preview
On the Apify free plan, the actor provides a preview of up to 10 job records.
The 10-record free-plan preview is a plan limit. It is not a pagination setting and it does not change the actor’s filtering or normalization behavior. A request with a larger maxItems value can still be constrained by the record availability allowed by the current Apify plan.
Pricing and PPE charging
The actor charges one PPE for each successfully emitted job record.
A job record is chargeable only after it has been successfully produced and emitted to the dataset. API requests, taxonomy loading, empty pages, failed detail requests, filtered-out listings, and jobs that are not emitted do not create a job-record PPE charge.
When fetchDetails is enabled, a successful detail request does not create a second PPE charge for the same job. The unit is one successfully emitted job record.
Local fixture test
The repository includes a fixture-based test for validating parsing and normalization without relying on the live eJobs API.
Install the project dependencies, then run:
python -m pytest -q
The fixture test should use saved API payloads from the test fixtures and should not require a browser, a live eJobs account, or access to the Cloudflare-protected website. This makes it suitable for local development and regression testing when the public API is unavailable or rate-limited.
Limitations
- The actor depends on public
api.ejobs.roresponses. The API is controlled by eJobs and can change without notice. - Only jobs and fields exposed by the public API can be collected.
- The actor does not access private employer data, authenticated user data, application submissions, or hidden jobs.
- Listing data can be incomplete. Salary, work model, county, company metadata, dates, and descriptions are not guaranteed for every job.
- Detail fetching can improve data quality but is not guaranteed. If an individual detail request fails, the actor falls back to the listing data for that job.
- A job can disappear, expire, or change while a paginated run is in progress.
- Taxonomy labels and IDs can be added, removed, renamed, or returned inconsistently by the upstream API.
- Salary values may use different currencies, ranges, gross/net conventions, or free-form employer text. Salary filtering only works when values can be interpreted reliably.
- Public API availability, rate limits, regional behavior, proxy availability, and network failures can affect throughput and completeness.
- This actor is designed for job-data collection and analysis. Users remain responsible for complying with applicable laws, eJobs terms, privacy obligations, and Apify platform rules.