Fuel Price Scraper - Petrol Prices & Stations AU/NZ
Pricing
from $1.80 / 1,000 results
Fuel Price Scraper - Petrol Prices & Stations AU/NZ
Scrape current petrol and fuel prices across Australia and New Zealand. Search by address or GPS coordinates to find nearby fuel stations, compare prices and collect station locations and fuel price data.
Pricing
from $1.80 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
13
Total users
2
Monthly active users
8 days ago
Last modified
Categories
Share
Fuel Prices ANZ Scraper: Live Station Prices Across Australia and New Zealand
Fuel Prices ANZ Scraper turns PetrolSpy's crowdsourced station data into a clean, structured feed. Search by suburb, postcode, address text, or raw coordinates to pull every nearby station with its live price per fuel type, trading hours, and contact details, across Australia and New Zealand, then export to JSON, CSV, Excel or XML, or read the results straight through the API.
Why This Scraper?
- Two ways to search. Type a suburb, city, postcode, or address into Suburb or City, or clear that field and use raw Latitude and Longitude for an exact center point instead. Suburb takes precedence when both are set.
- Offline AU/NZ geocoding. Suburb and postcode lookups resolve against local Australian and New Zealand postcode datasets, not a third party geocoding call.
- Price analytics built in. Every run adds a min/max/average/count price summary per fuel type, plus an optional cheapest station lookup for one fuel type.
- Full price detail per station. Every fuel type a station reports comes with its price, who reported it, and when it last changed, not just one headline number.
- Built for schedules. Incremental mode tracks a search over time and returns only the stations that are new or have actually changed, so a recurring monitor doesn't pay again for prices that haven't moved.
- Clear status, no billed error rows. A network or source problem ends the run with a plain status message and zero dataset rows, never a billable placeholder record.
- Real time HTTP access. Switch the actor into Standby mode to query it as a live HTTP endpoint instead of waiting on batch runs.
Use Cases
- Fuel price monitoring: track prices across a suburb or region on a schedule and see exactly what changed since last time.
- Cost optimization: find the cheapest station and fuel type near a given address right now.
- Fleet and logistics management: monitor refuelling costs across multiple depot suburbs at once.
- Market and pricing research: build a historical Australia/New Zealand fuel price dataset for analysis or forecasting.
- Price comparison apps and bots: feed live station data into your own site, app, or alert tool.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
stationId | "556fcfdccf79db30f9694c0b" |
name | "Metro Fuel Example Station" |
brand | "Metro" |
location.lat / location.lng | -33.8688 / 151.2093 |
location.address | "100 Example St" |
location.suburb | "Sydney" |
location.state | "NSW" |
location.postcode | "2000" |
phone | "+61255550000" |
prices.<fuelType>.price | 175.9 (per fuel type, e.g. U91, E10, U95, U98, DIESEL, LPG) |
prices.<fuelType>.updatedAt / .reportedBy / .relevant | 1739137133695 (PetrolSpy's own "price last changed" timestamp) / "Sample User" / true |
id, url, imageUrl | flat copies of the station id, a map link, and the brand logo image link (built from the source's brand icon file name, null when the station has none) |
address, suburb, state, postalCode, latitude, longitude | flat copies of the location.* fields |
u91Price, e10Price, u95Price, u98Price, dieselPrice, lpgPrice | flat copies of prices.<fuelType>.price (null when the station does not report that fuel) |
currency | AUD or NZD (prices are in cents per litre) |
distanceKm | straight-line distance from the search centre in kilometres |
tradingHours | opening/closing time pairs per day of week, or null when not reported |
isAdBlueAvailable | false |
verified | true |
icon / brandIcon | station and brand icon references, when PetrolSpy provides them |
searchContext.searchLocation | the resolved center point this run searched from |
searchContext.boundingBox | the neLat/neLng/swLat/swLng box the search covered |
searchContext.radiusKm | 5 |
changeType / changedFields | Incremental mode only: "NEW" / "UPDATED" / "UNCHANGED" / "EXPIRED" / "REAPPEARED", and the top level fields that changed, e.g. ["prices"] |
firstSeenAt / lastSeenAt | Incremental mode only: when this station first appeared for this state key, and when it was last seen |
Alongside the per-station dataset rows, each run also writes one summary record, OUTPUT, to the run's key-value store: the resolved search location, the radius searched, the total station count, a priceSummary of min/max/average/count per fuel type, and, when a Fuel Type Filter is set, a cheapest object naming the lowest priced station for that fuel type. This is a run-level snapshot, not a dataset record, so it always reflects everything the run found, even on Incremental mode runs where most per-station rows were suppressed.
How to Use
- Enter a suburb, city, postcode, or address in Suburb or City, or clear that field and set Latitude/Longitude for GPS coordinates instead. Suburb takes precedence over coordinates when both are set.
- Set Search Radius (km) to control how far from that point to search.
- Optionally pick a Fuel Type Filter to highlight the cheapest station for that fuel type.
- Click Start. Download the dataset as JSON, CSV, Excel or XML, or read the run's
OUTPUTsummary from the key-value store.
Search by suburb:
{"suburb": "Sydney CBD","radiusKm": 5,"fuelType": "U91"}
Search by postcode:
{"suburb": "3168","radiusKm": 10}
Search by coordinates:
{"suburb": "","lat": -33.8688,"lng": 151.2093,"radiusKm": 5,"fuelType": "E10"}
Recurring incremental monitoring:
{"suburb": "Auckland","radiusKm": 5,"incrementalMode": true,"stateKey": "auckland-daily"}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/fuel-prices-anz-scraper").call(run_input={"suburb": "Sydney CBD", "radiusKm": 5})for station in client.dataset(run["defaultDatasetId"]).iterate_items():print(station["name"], station["prices"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/fuel-prices-anz-scraper').call({ suburb: 'Sydney CBD', radiusKm: 5 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Supported start URL shapes
With searchMode set to startUrls, each link becomes one search. A link that cannot be used is skipped with a warning that names it. If some links fail to load, the others are still returned and the run is treated as partial (no EXPIRED rows); the run only fails when no link could be loaded.
| URL shape | Supported | Example |
|---|---|---|
/map/location/<country>/<state>/<postcode>/<name> | Yes (AU and NZ; the country in the link decides which postcode list is used) | https://petrolspy.com.au/map/location/au/nsw/2000/sydney, https://petrolspy.com.au/map/location/nz/wellington/6011/wellington |
/map/latlng/<lat>/<lng> | Yes | https://petrolspy.com.au/map/latlng/-33.8688/151.2093 |
/map/station/<id> | No: skipped with a warning; use a location or coordinate link | |
/map/graph/... | No: skipped with a warning |
Resume and recurring updates (Incremental mode)
- Resume (
resumeFromRunId) only has an effect together with Incremental mode, and only the first time a state key has no saved baseline yet: it seeds that baseline from a previous run's or dataset's stations, so they don't lookNEWagain on day one of monitoring. This actor's search is a single request with no pagination, so there is no mid crawl to resume. - Incremental mode (
incrementalMode) is for scheduling the same search again and again. The first run for a state key returns every station asNEW. A station identical to last time is not returned, and not billed, unlessemitUnchangedis on, in which case it comes back markedUNCHANGED. A station whose data changed comes back markedUPDATED, withchangedFieldsnaming the top level fields that moved, fuel prices included. - Ignore fields when detecting changes (
ignoreFieldsForChanges) lists fields to leave out of change detection (they stay in the output). Nested paths work, e.g.prices.*.reportedBy. Nothing is ignored by default: every fuel price and its price timestamp count, a field flipping to or from empty does not, and fields newly added to the output never cause a one-off wave ofUPDATEDrows. EXPIREDandREAPPEAREDare not part of the normal cycle. A station missing from the search area is only recorded as gone whenemitExpiredis on for that run: it is returned once as a tombstone row markedEXPIRED, and its stored state is flipped to "not present".REAPPEAREDonly follows that: a station that was tombstoned this way and is then found again on a later run comes back markedREAPPEARED, keeping its originalfirstSeenAt. IfemitExpiredis never switched on, a station can never reachEXPIREDorREAPPEARED; it only ever cycles betweenNEW,UPDATED, and suppressedUNCHANGED.- State is tracked separately per
suburb/lat+lng,radiusKm, andfuelType, so different search setups never share a baseline automatically. SetstateKeyto name a monitoring campaign explicitly, or to deliberately share state across differently configured runs. - Search centre changes (one-time). A plain suburb or postcode search now centres on the locality's delivery-area centroid (for example
Sydneymoved about 9 km, to the CBD), and explicitlat/lngnow win over the defaultSydneysuburb. A recurring Incremental search set up before this change can therefore see a one-time set ofNEW(billed) stations on its first run after the update and, withemitExpiredon,EXPIREDrows for stations that left the new area. Set a newstateKeyto start a clean baseline, or leaveemitExpiredoff for that run. - Stations are matched between runs by
stationId, falling back to abrand+addresscomposite for the rare record missing one. - The diff ignores a field flipping between a real value and missing/
nullon its own, so that alone never counts as a change; a genuine change elsewhere in the same station is still caught.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
suburb | string | "Sydney" | Suburb, city, postcode, or address text to search. Set to "" (or leave the default Sydney) to use lat/lng instead; a non-default suburb takes precedence when both are given. |
lat | number | none | Latitude, used with lng instead of suburb. |
lng | number | none | Longitude, used with lat. |
latitude | number | none | Alias of lat, used only when lat is empty. |
longitude | number | none | Alias of lng, used only when lng is empty. |
searchMode | string | "location" | location uses the suburb/coordinates above. startUrls searches the map links in startUrls instead. |
startUrls | array | none | Map links, see "Supported start URL shapes" below. Only used when searchMode is startUrls. |
brands | array | none | Only return stations of these brands (case-insensitive). |
sortBy | string | none | price (for the fuelType below, U91 when empty), distance or name. Stations missing the sort value come last. Empty keeps the source order. |
sortOrder | string | "asc" | asc or desc, used with sortBy. |
maxItems | integer | 1000 | Cap on stations returned for the whole run (maximum 100000), applied after brands and sorting. 0 uses the maximum. A capped or brand-filtered run never marks the stations it left out as EXPIRED. |
radiusKm | number | 5 | Search radius in kilometers from the center point (1 to 50). A large area that the data source refuses in one request is automatically fetched in four parts and merged, so the full range works. |
fuelType | string | none | Optional (U91, E10, U95, U98, Diesel, or LPG). It does not remove stations: it selects the fuel for the cheapest highlight and for sortBy: price. Every fuel a station reports is still returned. |
proxy | object | {"useApifyProxy": false} | Off by default (direct connection); this is a genuine default, not just a UI prefill. Turn on Apify Proxy with the Residential group if requests start failing from a given region. |
resumeFromRunId | string | none | Paste a previous run ID or dataset ID to seed Incremental mode's baseline from it. Has no effect without Incremental mode on. |
incrementalMode | boolean | false | Return only new and changed stations on repeat runs of the same search. |
stateKey | string | none | Name a monitoring campaign, or share state across differently configured runs. Leave empty to derive a key automatically from the search scope. |
emitUnchanged | boolean | false | Also return, and bill, stations unchanged since the last run, marked UNCHANGED. |
emitExpired | boolean | false | Also return, and bill, stations no longer found this run, marked EXPIRED. |
ignoreFieldsForChanges | array | none | Field names or nested paths (prices.*.reportedBy) to leave out of change detection. Incremental mode only. |
mcpConnectors | array | none | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: page under which item pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run (1 to 1000). Does not affect the dataset. |
Either suburb or both lat and lng must be provided.
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"stationId": "556fcfdccf79db30f9694c0b","name": "Metro Fuel Example Station","brand": "Metro","location": {"lat": -33.8688,"lng": 151.2093,"address": "100 Example St","suburb": "Sydney","state": "NSW","postcode": "2000"},"phone": "+61255550000","prices": {"U91": { "price": 175.9, "updatedAt": 1739137133695, "reportedBy": "Sample User", "relevant": true },"E10": { "price": 172.9, "updatedAt": 1739137133694, "reportedBy": "Sample User", "relevant": true },"DIESEL": { "price": 189.9, "updatedAt": 1739137133694, "reportedBy": "Sample User", "relevant": true }},"tradingHours": null,"isAdBlueAvailable": false,"verified": true,"icon": "metro-icon","brandIcon": "metro-brand-icon","searchContext": {"searchLocation": { "lat": -33.8688, "lng": 151.2093, "address": "Sydney, NSW 2000, AU" },"boundingBox": { "neLat": -33.82, "neLng": 151.25, "swLat": -33.91, "swLng": 151.16 },"radiusKm": 5}}
Plan Requirement
This actor runs on any Apify plan with an active account; there is no separate feature gate. The default connection (no proxy) is what the actor is tested with, and a plain request from a hosting-provider IP was served normally, so no proxy is required for typical searches. I have not tested every Apify proxy group, so if requests start failing from a specific region, turn on Apify Proxy under Proxy settings and choose the Residential group. Apify Proxy needs a plan that includes proxy access: if the proxy connection is refused for your account, the run ends as failed with a status message saying so, rather than returning an empty dataset. Use Search Radius and Incremental mode with emitUnchanged off to keep the size, and cost, of recurring runs down.
FAQ
How much does it cost?
You are billed per result: each station row written to the dataset is one apify-default-dataset-item event, plus a single apify-actor-start event per run. The current rates are on the Pricing tab of the actor page. Rows that Incremental mode suppresses as unchanged are not returned and not billed unless emitUnchanged is on, and a failed run writes no billed placeholder row.
Is it legal to scrape fuel prices from PetrolSpy?
This actor collects publicly available station and price data. You are responsible for how you use it: follow PetrolSpy's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices are community reported and change frequently; treat them as indicative, not guaranteed accurate at the moment you read them.
Can I get only new or changed prices on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. The first run returns everything as NEW; later runs return only stations that are new or changed, and unchanged ones are skipped and not billed. EXPIRED and REAPPEARED only show up once emitExpired has been switched on for at least one run; see "Resume and recurring updates" above for the exact sequence.
Why does my search return nothing, or the wrong area?
The most common cause is the suburb or postcode text not matching the local AU/NZ postcode dataset closely enough (for example a full street address with no recognizable suburb). Try a plain suburb name or postcode, or switch to lat/lng coordinates for an exact center point. resumeFromRunId is unrelated to this: it only seeds Incremental mode's very first baseline, and does nothing on its own without incrementalMode turned on.
Why did my run fail instead of returning an empty dataset?
Yes, when no usable result could be produced. The run ends as failed, with a plain text status message and no billed placeholder row, in these cases: neither suburb nor both lat and lng were provided (or the suburb text could not be resolved); PetrolSpy's webservice could not be reached this run (re-run it; if it persists, try Apify Proxy with a Residential exit in Australia); the search area is too large for the data source even after splitting it (lower radiusKm); every link in startUrls was unusable or failed to load; the proxy connection you selected was refused for the account; Incremental mode combined with resumeFromRunId when the chosen stateKey already has saved state from an earlier run; or a resumeFromRunId that points to a run or dataset this account cannot read. A search that loads fine but has no stations nearby succeeds with zero rows.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more travel data?
Pair this actor with these related scrapers from the same team:
| 🧩 GasBuddy Scraper Scrape GasBuddy station by station across the US and Canada. Returns brand, address... | 🏠 View.com.au Scraper Scrape View.com.au properties for sale, rent and recently sold across Australia. Search... |
| 🏠 Real Estate AU Extract detailed Australian real estate property listings with 30+ structured fields... | 🤖 AU Residential Property Scrape Australian property listings with rich detail-page enrichment. Each listing record... |
| 🏠 Commercial Property AU Scraper Scrape Australian commercial property listings with full detail data. Extract... | 🏠 DDproperty Thailand Scraper Scrape DDproperty.com Thailand listings at scale. Extract prices, property features... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.