Methodology

Every statistic Finjobsly publishes about its own job listings is a direct, reproducible count over its own database -- nothing about Finjobsly’s own data is estimated, modelled, or purchased from a third party. This page documents exactly how a listing enters that database, how it is classified, and how each published figure is calculated -- so every number on the Hiring Indexand elsewhere in Finjobsly’s research can be traced back to a specific, auditable rule. A small set of free, clearly labelled third-party benchmarks also appear as separate market context (see below); they are never blended with, or used to recalculate, a Finjobsly figure.

Where jobs originate

Most listings come from an automated scraper that runs against a per-company source list (scraping_sources, one row per company careers page). Each source is scraped by an adapter matched to how that company publishes its jobs: the Greenhouse and Lever adapters call those ATS platforms’s own public, documented, no-auth JSON APIs; Ashby and Workable use the equivalent public posting APIs for those platforms; a generic JSON fallback handles sources with a nonstandard API shape; and a generic HTML fallback (which respects robots.txt) reads a careers page directly, preferring embedded schema.org/JobPosting structured data before falling back to heuristic parsing.

A scheduled job checks every 15 minutes for sources that are due to be scraped again, based on each source’s own configured frequency (12 hours by default, tuned per source). Every scrape run is logged with its outcome and job counts, and a source’s listings are never marked expired as a side effect of that source’s site being temporarily unreachable.

Separately, employers can list a role directly through Finjobsly’s own job-posting flow rather than being scraped from an external source.

Deduplication

When a scrape finds a job, it is matched against existing rows first by that source’s own listing ID (source_id + external_id), and if no match is found, by an exact match on company name, title and location. A match updates the existing row rather than creating a duplicate.

To avoid unnecessary writes (and unnecessary re-classification), each normalized listing has a hash computed over the fields that matter for change detection; if a re-scrape produces the same hash as the stored row, nothing is written. Listings are never hard-deleted -- a listing the scraper stops finding on its source for three consecutive clean runs is soft-deleted (marked expired) rather than removed, so historical figures that already counted it stay accurate.

Definition of an active vacancy

A listing counts as active if its status is “active” or “published” and it has not expired. Expiry is resolved in order: if the listing has an explicit expiry date, that date governs. Otherwise, it expires 30 days after the later of its posted date or the date it was last reconfirmed live during a scrape -- so a listing with an old posted date but a recent reconfirmation is still counted as active, not stale.

This is the one definition of “active” used everywhere on Finjobsly that counts jobs -- the Hiring Index, category pages, and city pages all filter against the same rule, so a total never disagrees with itself across pages.

How verticals are classified

Each active listing is checked against every fintech sub-vertical’s keyword rules (the same rules that power Finjobsly’s category pages, e.g. /fintech-jobs, /ai-finance-jobs), applied to the listing’s enriched title and description text. A listing can match more than one sub-vertical, or none -- so vertical counts do not necessarily sum to the total active count, and are not mutually exclusive.

How job titles are normalized

For “most in-demand roles” and salary insights, each listing’s free-text title is mapped to one of a fixed set of canonical role buckets (for example Software Engineer, Product Manager, Compliance, Data Scientist, Fraud Analyst) using keyword matching against the title text.

A title that does not clearly match any bucket is excluded from these role-based statistics rather than being folded into a generic “other” count that would misrepresent demand for a specific role.

How locations are normalized

“Top hiring locations” groups listings by country, using each listing’s structured country code (mapped to a display name, e.g. GB to United Kingdom). Listings without a resolvable country code are not counted toward this statistic.

Salary insights use a similar country-level grouping, resolved from the listing’s country code and its raw location text together, so a salary figure is never reported at a finer geographic grain than the underlying data actually supports.

How skills are extracted

Requested skills come directly from each listing’s structured skills field, populated when the listing is first scraped and normalized. Skills are counted as they appear (case-insensitively, so “SQL” and “sql” count together), not re-interpreted or expanded into related terms.

How salary statistics are calculated

Only listings with both a minimum and a maximum pay figure are eligible, and only when both figures fall within a plausible annual-pay range (between $5,000 and $2,000,000) -- this filters out data-entry errors and placeholder values without discarding genuinely low or high, but real, figures.

Each eligible listing contributes the midpoint of its own minimum and maximum. Listings are grouped by normalized role and country; the published figure for a group is the median (the middle value) of that group’s midpoints, not an average, so a small number of outlier listings cannot skew the headline number.

A role/country group is only published once it has at least 3 eligible listings (see Sample thresholds below).

Sample thresholds

Roles, skills and locations are only listed once they appear in at least 3 active listings -- below that, a count is more likely to reflect one employer’s wording than a real market pattern, so it is withheld rather than published as if it were representative.

Top hiring employers are the one exception: every employer with at least one active listing is eligible, because a company’s own listing count is an exact fact about that company, not an estimate that needs a minimum sample to be trustworthy.

Salary insights require at least 3 eligible listings per role/country group, per the calculation above.

How often data updates

Active-listing figures (the Hiring Index and everything derived from it) are recomputed daily: the current calendar month’s row is overwritten each day as listings change. Once a month ends, its row is locked and never written to again, so a past edition of the Hiring Index never changes retroactively.

Finjobsly also records one immutable snapshot per day, separate from the monthly row, so that once enough days of history have accumulated, real 7-day, 30-day and 3-month trend changes can be computed from actual stored history rather than a live-vs-live comparison. Finjobsly does not publish a trend figure before it has enough real history to support one -- see the Trend section on the Hiring Index page.

Third-party market context

Alongside the Finjobsly-sourced statistics above, Finjobsly’s Shared Intelligence API surfaces a small set of free, licensed third-party benchmarks as separate market context -- never blended into, averaged with, or used to recalculate any Finjobsly figure. Each is refreshed on a fixed schedule (monthly, or weekly for GDELT) directly from the source’s own public API, cached, and labelled with its retrieval date and source URL:

  • World Bank Open Data-- a purchasing-power (PPP) conversion factor per salary-alpha country bucket. The EU bucket uses Germany’s figure as a single representative Eurozone economy (the World Bank does not publish a Euro-area aggregate for this indicator); this is disclosed on every reading.
  • UK Office for National Statistics (ONS) -- a UK-wide job vacancies count and a UK Average Weekly Earnings figure. The earnings series is a labelled substitute for ONS ASHE (which has no JSON API), stored and disclosed under its own series ID rather than presented as ASHE.
  • Eurostat-- an EU27-wide job vacancy rate. Confidential or suppressed periods in Eurostat’s own data are skipped in favour of the most recent genuinely published period; Finjobsly never estimates a figure Eurostat has withheld.
  • US Bureau of Labor Statistics (BLS) OEWS-- not currently available. BLS’s public API has been unreachable from Finjobsly’s infrastructure; rather than substitute an estimate, this benchmark is simply absent until a working access path exists.
  • SEC EDGAR (including Form D exempt-offering filings) and GDELT DOC 2.0 news/media tone -- used only inside Finjobsly’s employer-facing Premium Intelligence product (the Burn-to-Hire funding signal and Brand Employer Score media tone respectively), not on public research pages. Both are real, keyless or free-tier public data sources; a company with no qualifying filing or coverage gets no fabricated figure in its place.
  • Companies House and the UK FCA Register-- per-company registry reference checks (name, status, incorporation date), shown to Finjobsly admins as a labelled reference signal. An automated name match never overrides Finjobsly’s own human-reviewed company verification.

Every third-party figure above is read from a cached, timestamped copy of what that source last published -- never fetched live on a page request, and never merged into a Finjobsly-sourced statistic. If you see a third-party figure and a Finjobsly figure side by side, they are always two distinct numbers from two distinct populations.

See the numbers this produces

The Hiring Index applies every rule on this page to Finjobsly’s current listings.

View the Hiring IndexIntelligence hub