Gas Safe Register Businesses - UK Gas Firm Leads
Pricing
$4.00 / 1,000 per registered business returneds
Gas Safe Register Businesses - UK Gas Firm Leads
From $4 per 1,000 businesses, no start fee. Search the official Gas Safe Register by UK postcode: business name, registration number, full address, phone and email. Measured on 95 live businesses: 96.8% email, 95.8% phone, 100% address. Optional appliance qualifications. Deduped by reg number.
Pricing
$4.00 / 1,000 per registered business returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Gas Safe Register Businesses — UK Gas Firm Leads
Search the official Gas Safe Register — the statutory register every legal gas business in Great Britain must be on — by UK postcode, and get back the business, its registration number, its full postal address, its phone, its email, and (optionally) every appliance it is qualified to work on. Deduplicated by registration number, so a firm that turns up in five of your postcodes is delivered once and charged once.
$4 per 1,000 businesses. No run-start fee. A run that finds nothing costs nothing.
What you actually get — measured, not estimated
Counted on 95 distinct businesses (100 business cards across 10 results pages, deduplicated by
registration number), captured live on 2026-09-16 from five postcodes spanning central London,
Manchester, Land's End, Snowdonia and the Channel Islands. The fixtures are in fixtures/ and
offline_validate.mjs re-counts every number below from those bytes on each run.
| Field | Fill | Notes |
|---|---|---|
registrationNumber | 100.0% | 95/95. The dedupe key and the row's identity. |
businessName | 100.0% | 95/95 |
premise | 100.0% | 95/95 — first address line (often the trading name again) |
town | 100.0% | 95/95 |
postcode | 100.0% | 95/95, and all 95 are syntactically valid UK postcodes |
fullAddress | 100.0% | 95/95 — the lines joined, placeholders dropped |
distanceMiles | 98.9% | 94/95, miles from the postcode you searched |
street | 97.9% | 93/95 |
email | 96.8% | 92/95 |
telephone | 95.8% | 91/95 |
outOfHoursAvailable | 58.9% true | 56/95 advertise out-of-hours cover; the rest is a real false, not a gap |
county | 32.6% | 31/95 — the register prints it only for some addresses |
locality | 7.4% | 7/95 — thin, and said so rather than quietly shipped |
- Phone AND email: 92.6% (88/95). Phone OR email: 100% (95/95) — every business in the sample published at least one way to reach it.
- Where a business publishes no email the register prints a literal
-. Every single unfilled contact cell in the sample was exactly that string. This actor turns it intonull; the string"-"never reaches your dataset, because a"-"in an email column is what breaks a mail merge and inflates a fill percentage at the same time.
Appliance qualifications (opt-in)
Switch on includeQualifications and each row also carries the register's "Qualified to work
on" table: every appliance category and specific qualification the business holds, each flagged
for Natural Gas, LPG and Building Regulations, split into Domestic and Commercial.
Measured on 11 distinct businesses on 2026-09-16 (7 from the captured fixtures, 4 more on a live Glasgow run): between 26 and 82 qualification rows each, and between 17 and 62 specific appliance qualifications actually held. All 11 held at least one.
{"serviceType": "Domestic","service": "CARAVANS - Closed Flue Gas Fires DOM LPG","isCategory": false,"naturalGas": null,"lpg": true,"buildingRegs": null}
A cell has three states, and this actor keeps all three: a tick is true, a cross is false,
and a blank is null. Collapsing blank into false would invent an assessment the register never
made.
It costs time, which is why it is off by default. One extra page load per business, measured live at about 1.9 seconds each at the default 1,200 ms delay — so a full 50-business postcode takes roughly 95 extra seconds. It adds no charge: those pages belong to a business you are already paying for.
(An earlier measurement of ~9 s per business was an artefact of a wrong approach — walking back to the results list between each fetch. That approach is also incorrect, not just slow: see limit 5. Each business's qualification link is taken off the results bytes already in hand and followed directly, which was verified live to land on the right business 4 times out of 4.)
A sample row
{"registrationNumber": "211205","businessName": "Plumbforce Direct Ltd","telephone": "02038416530","email": "scott@plumbforcedirect.co.uk","premise": "Plumbforce Direct Ltd","street": "4 Old Park Lane","locality": null,"town": "LONDON","county": null,"postcode": "W1K 1QW","fullAddress": "Plumbforce Direct Ltd, 4 Old Park Lane, LONDON, W1K 1QW","distanceMiles": 0.36,"outOfHoursAvailable": true,"serviceType": "Domestic","searchPostcode": "SW1A 1AA","resultPage": 1,"qualificationCount": null,"qualifiedFor": null,"qualifications": null,"scrapedAt": "2026-09-16T21:02:38.092Z"}
Who buys this
- Installer-network recruiters (the BOXT / Heatable / Hometree shape) building a vetted engineer panel in a given catchment.
- Manufacturer loyalty schemes (Worcester Bosch, Vaillant Advance and similar) recruiting registered installers — the qualification table tells you who can actually fit the product.
- Merchants and distributors opening trade accounts in a new branch catchment.
- Trade insurers and warranty providers underwriting registered gas businesses.
What makes the row defensible is what Google Maps cannot give you: a statutory registration number, and the appliance-by-appliance qualification behind it.
The honest limits
Read these before you plan a national sweep. They are not caveats bolted on at the end; they are the shape of the source.
1. There is a hard ceiling of 50 businesses per search, and no national total
Every postcode search — central London, Shetland, Land's End, everywhere tried — answers
Showing 1 to 10 of 50 results across exactly 5 pages of 10. There is no page-size override
and the register never publishes how many businesses it holds in total. Coverage is a function of
how densely you space your postcodes, not of paging deeper.
Measured in central London on 2026-09-16: those 50 slots were used up within 1.24 miles. In a dense metro, space your postcodes roughly 2 miles apart or the grid under-covers. A rural search reaches far further for the same 50, so one postcode covers much more ground.
This actor cannot tell you "all UK gas businesses". It tells you who the register returns for
the postcodes you asked for. RUN_SUMMARY.coverageCaveat says the same thing inside every run.
2. The register answers a rate-limited request with HTTP 200 and a block page
This is the nastiest thing about the target and the reason this actor is built the way it is. When you go too fast, the site does not return 429 or 403 — it returns HTTP 200 carrying an Imperva/Incapsula block page. A scraper that trusts the status code parses zero rows and reports "no gas businesses near this postcode": confident, wrong, and billable.
Every response body is sniffed for the block markers before a zero-row parse is believed, and a
block fails the postcode (Actor.fail() if it happens to every postcode) instead of being
reported as an empty result. A genuinely empty page is distinguishable and is accepted: it still
carries its Showing … results envelope. Both cases are in the offline test suite, against real
captured bytes.
Defaults are set to the rate that measured clean: one search every 15 seconds, 1.2 s between
pages inside a search. Three back-to-back searches tripped the limiter; it recovered after about
50 seconds idle. Lowering searchIntervalSecs will not make your run faster, it will get it
blocked.
3. It needs a real, headful browser — which is what it costs
Measured on 2026-09-16 against this host:
| Client | Result |
|---|---|
got-scraping / curl, full Chrome header set | 403, ~860–968-byte Incapsula body on every path |
Playwright Chromium, headless: true | 403, 858-byte Incapsula body |
Playwright Chromium, headless: false | 200 — the reese84 interstitial solves itself in ~2.5 s and the real page loads |
So the actor runs a headful Chromium on the Apify Playwright image's Xvfb display, at 4096 MB. That is the cost of this lane, and it is priced in.
One more measured subtlety: clicking the search button with a synthetic mouse event produced a
403 on the results redirect (the site's cookie-consent overlay sits over the button). Setting
the field values and calling the form's own requestSubmit() produced a clean 200. This actor
therefore never clicks through a consent dialog on your behalf.
4. UNMEASURED: Apify's own proxy pools
The build was verified end to end from one ordinary IP (which happened to be a US one — the site
did not geo-block it). It was not measured through Apify's DATACENTER or RESIDENTIAL pools,
and no claim is made that it clears Imperva from them. proxyConfiguration therefore defaults to
RESIDENTIAL, which is the right bet for an Imperva-fronted site, and if you see
RUN_SUMMARY.blockedPostcodes filling up, point it at your own proxy. A blocked postcode is
reported as blocked; it is never silently converted into "found nothing", and nothing is charged
for it.
5. The result list re-sorts between requests, so never re-read it to find a row
Requesting the same search page twice does not reliably return the same businesses in the same
order. Measured: ten index-based visits to "the Nth result" returned only seven distinct
businesses. The actor never re-reads a list to find a row again — it takes each business's own link
off the page bytes it already parsed, and dedupes everything by registration number. Do not treat
the envelope's 50 as a promise of 50 distinct rows.
6. A valid UK postcode can still be unknown to the register
The register runs its own postcode lookup when you submit. A postcode that is perfectly well-formed can come back with "That postcode or town was not found in our records. Please try again with a full UK postcode." — measured on B1 1AA, which is a textbook-shaped Birmingham postcode.
When that happens the page does not navigate at all. This actor reads the register's own message,
reports the postcode in RUN_SUMMARY.rejectedPostcodes, charges nothing for it, and moves on
to the next one. It does not force the submit through (that was tried: it navigates to a
64,122-byte page with no results envelope on it — a junk page there is no honest way to read). A
refused postcode is detected in about 4 seconds, not by burning a navigation timeout.
7. What is deliberately not shipped
- No engineer-level records. The register has a second surface behind "View Engineers" with
named individual engineers. It is out of scope for this actor: it is a different function
(people, not businesses), it multiplies the request count against a hard rate limit, and the
contact data there is thin — the engineer record inspected during recon printed
Telephone: n/a. The rich contact data is at business level, which is the right level for a lead list anyway. - No URLs back to the register. Every deep link on this site is addressed by an encrypted
eptoken that expires three minutes after it is minted. Shipping one would ship a field that is dead by the time you open the dataset, so there is noregisterUrlfield. Look a business up by its registration number instead. - No ratings or review counts. The register does not publish them.
8. Robots and access
robots.txt (fetched 2026-09-16) disallows /aspnet_client/, /bin/, /config/, /data/,
/macroScripts/, /umbraco/, /umbraco_client/, /usercontrols/, /xslt/, /engineer/ and
*?wcag*. The paths this actor uses — /find-an-engineer-or-check-the-register/, /widgetfc,
/findbusinessresults, /businesscompetencies — are not disallowed. Everything read is public
data that the register publishes so consumers can check their installer.
Input
| Field | Default | What it does |
|---|---|---|
postcodes | (required) | One search each. Anything that is not a valid UK postcode is rejected before any request, listed in RUN_SUMMARY.invalidPostcodes, and never charged. |
serviceType | Domestic | Domestic or Commercial — separate registers on the site. |
maxPagesPerPostcode | 5 | 10 businesses per page; 5 is the register's own ceiling. |
maxBusinesses | 1000 | Hard stop on delivered (= charged) rows. 0 means no cap. Checked against rows produced, so it never overshoots. |
includeQualifications | false | The appliance-qualification table. ~9 s per business. No extra charge. |
requireContact | false | Drop rows with neither phone nor email — before delivery, so they are never charged. |
requireEmail | false | Drop rows with no email — same. |
searchIntervalSecs | 15 | The measured-clean pace. |
requestDelayMs | 1200 | Between pages inside one search. |
proxyConfiguration | Apify RESIDENTIAL | See limit 4. |
The default input is deliberately a one-postcode, one-page, 10-business run so a first try costs four cents and finishes in under a minute.
Output
Rows land in the dataset (three views: overview, contacts-only, qualifications). RUN_SUMMARY in
the key-value store reconciles the run and keeps every kind of "nothing" apart:
invalidPostcodes— not a UK postcode; rejected before any request was maderejectedPostcodes— a valid postcode the register's own lookup does not knowblockedPostcodes— the site's edge refused us; not "no businesses there"unreachablePostcodes— a transport or parse failure, with the reasonpostcodesThatReturnedNothing— a real, empty answerexcludedByRequireEmail/excludedByRequireContact— your filters, never chargedcardsParsed,duplicatesSkippedByRegistrationNumber,cardsWithoutRegistrationNumberdeliveredandcharged— always equalperPostcode— the same breakdown per search
Billing
One event, business-scraped, at $0.004 per distinct business delivered. No start fee, neither
Apify auto-event. Charging rides on Actor.pushData(items, event), which bills per item and pushes
only what your charge cap allows — so delivered equals charged, and a cap never overshoots by a
batch.
Verified live, end to end
These modules — not a prototype of them — were driven against the live site on 2026-09-16 from an ordinary IP:
| Run | Result |
|---|---|
EH1 1RE, 5 pages | 50 cards parsed, 50 distinct businesses, 0 duplicates, 0 cards missing a registration number, in 13.6 s (38.3 s including browser launch and the Imperva clear) |
NE1 4ST + CF10 1EP, 2 pages each | 40 distinct businesses, 39/40 phone, 39/40 email, no "-" leaked into any row |
B1 1AA | refused by the register's own lookup, detected in 3.9 s, reported and not charged |
G1 1XW qualifications | 4 businesses, 26–76 qualification rows each, 4/4 landed on the right business, 1.9 s each |
Verify it yourself
$node offline_validate.mjs
No network, no platform, no node_modules. It runs every assertion in this README against the
captured bytes in fixtures/ — including the 858-byte block body, the 6,183-byte interstitial, a
real results page holding zero businesses, and the triple-URL-encoded links whose encoding must
survive the walk untouched.