GasBuddy Scraper: Fuel Prices, Stations & Reviews
Pricing
from $1.00 / 1,000 station results
GasBuddy Scraper: Fuel Prices, Stations & Reviews
Scrape GasBuddy station by station across the US and Canada. Returns brand, address, coordinates, phone, amenities, open status, ratings and reviews, plus the current cash and credit price for every fuel grade with who reported it and when. Incremental mode tracks price changes between runs.
Pricing
from $1.00 / 1,000 station results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Features
Share
GasBuddy Scraper: Station Fuel Prices, Amenities & Reviews
GasBuddy Station and Fuel Price Scraper turns GasBuddy into station level fuel price data for the United States and Canada. For every station it returns the brand, the full postal address, coordinates, phone, amenities, opening status, member rating and reviews, and the current cash and credit price for every fuel grade the station sells, together with when that price was reported and which member reported it. Search by postal code, city, state or a coordinate radius, or paste station and search links directly, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Station level pricing, not a state average. Every fuel grade a station sells comes back with its own cash price, credit price, formatted price, member discount, and who reported it and when.
- Two ways to search. Look up a postal code, a city and state, a state wide sweep, or a coordinate pair for a radius search, or paste station and search links directly.
- Filters beyond the site's own. Brand (checked against GasBuddy's own brand list), fuel grade and price age narrow the search itself; price range, star rating, amenities, open now and has a reported price are applied on top of the results, including amenity filtering GasBuddy does not offer.
- Reviews on demand, priced fairly. The first page of member reviews for every station arrives free with the search itself. Only asking for more rows than that page holds triggers an extra, clearly priced request.
- Built for price monitoring. Incremental mode returns only the stations whose price (or anything else) actually moved on a scheduled run, with
changedFieldsnaming exactly what changed, turning a live snapshot into a price history over time. GasBuddy does not publish a per-station price time series, so every row is the current snapshot; schedule runs in incremental mode to build your own history. - Resume an interrupted crawl, or split a very large sweep across several runs with a page position on a pasted search link, without re-reading or re-paying for rows a previous run already returned.
- Fails loudly instead of lying. If the site refuses every request, the run stops with a clear message so "no stations here" is never confused with "the read was blocked".
Use Cases
- Fuel price comparison apps: pull the cheapest current price per grade across an area to power a station finder or a price alert tool.
- Market and competitor pricing research: track brand level and area pricing over time with Incremental mode, without re-paying for stations whose price has not moved.
- Fleet and logistics fuel planning: filter by grade, price range and amenities such as diesel or truck stop access along a route.
- Site selection and local research: amenities, opening status and member ratings for a station or a whole area.
- Data journalism and price trend research: build a dataset of regional price movement using scheduled Incremental runs and
changedFields.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
stationId | "90210001" |
name | "Example Fuel Stop" |
url | GasBuddy link to the station |
brand / brandId | "Example Fuel Brand" / "501" |
addressLine1 / city / state / postalCode / country | "400 Sample Ave" / "Example City" / "TX" / "00000" / "US" |
latitude / longitude | 29.9012 / -95.4123 |
phone | "555-010-0100" |
distance / distanceMiles | "1.2mi" / 1.2 (coordinate searches only) |
openStatus | "open" |
amenities | ["C-Store", "Pay At Pump", "Air Pump"] |
fuels | ["regular_gas", "midgrade_gas", "premium_gas", "diesel"] |
prices | one row per grade: cash price, credit price, formatted price, member discount, reported at, reported by |
lowestPrice / lowestPriceFuelType | 3.29 / "regular" |
lastPriceReportedAt / lastPriceReportedBy | "2026-02-01T09:00:00.000Z" / "sample-member" |
priceUnit / currency | "dollars_per_gallon" / "USD" |
starRating / ratingsCount | 4.1 / 56 |
reviewCount / reviews | 12 / member reviews (when Include member reviews is on) |
priceTrend | the area price context GasBuddy publishes beside the station: today's average, today's low, direction |
cardPaymentAvailable | true |
hasActiveOutage | false |
changeType / changedFields | "UPDATED" / ["lowestPrice", "prices"] (Incremental mode only) |
firstSeenAt / lastSeenAt | timestamps this actor tracked the station (Incremental mode only) |
Every record also carries allBrands, addressLine2, openingHours, nextOpenAt, nextCloseAt, amenityIds, topSpotters (the crowd's top price spotters), offers (member discount offers), emergencyStatus, isTruckFleetSite, isEnterprise, searchTerm, searchLocation and scrapedAt.
How to Use
- Pick a mode:
search(postal code, city, state or coordinate pair) orurl(paste station or search links). - Fill in Locations or the links you pasted, then narrow the results with the filters: brand, fuel grade, price age, price range, star rating, amenities, open now, or has a reported price.
- Set Max stations (and, if you need to, Max pages and Max reviews per station) to control run size and cost, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Search a city with a brand and grade filter:
{"mode": "search","locations": ["Example City, TX"],"brands": ["Shell"],"fuelType": "regular","maxStations": 20}
Coordinate radius search, open stations only:
{"mode": "search","locations": ["40.0,-75.0"],"maxDistanceMiles": 5,"openNowOnly": true}
Paste station and search links:
{"mode": "url","urls": ["https://www.gasbuddy.com/station/29981","https://www.gasbuddy.com/home?search=00000"]}
Recurring price monitoring for the same search:
{"mode": "search","locations": ["Example City, TX"],"incrementalMode": true,"maxStations": 0}
URL mode: accepted link shapes
URL mode with the links field left empty scrapes one example of each shape in the table below. Replace the list with your own links to scrape those instead. A link that is not recognised is named in the run log and skipped, never silently ignored.
| Shape | Example | What you get |
|---|---|---|
| Station page | https://www.gasbuddy.com/station/29330 | That station, always returned even when the filters would exclude it. |
| Bare station id | 29330 | The same station. |
| Station page, host without www or mobile host | https://gasbuddy.com/station/29330, https://m.gasbuddy.com/station/29330 | The same station, read by its stable id. |
| Search by postal code | https://www.gasbuddy.com/home?search=78701 | Stations around that postal code. |
| Search, later page | https://www.gasbuddy.com/home?search=Austin%2C%20TX&page=2 | The walk starts at that page. |
| Coordinate search with row offset | https://www.gasbuddy.com/home?search=30.2672,-97.7431&fuel=1&cursor=50 | A radius search starting at row 50. |
| State or city price page | https://www.gasbuddy.com/gasprices/texas/austin | Read as a search for that city and state. |
A link that starts deeper with &page= or &cursor= covers only part of the area, so a recurring run never marks stations outside that slice as expired.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/gasbuddy-com-scraper").call(run_input={"mode": "search","locations": ["Example City, TX"],"maxStations": 20,})for station in client.dataset(run["defaultDatasetId"]).iterate_items():print(station["name"], station["lowestPrice"], station["lowestPriceFuelType"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/gasbuddy-com-scraper').call({mode: 'search',locations: ['Example City, TX'],maxStations: 20,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
How Max stations and Max pages shape a run
Max stations stops the whole run after that many matching stations, shared across every location in the run so each one contributes instead of the first location spending the whole budget. Max pages stops each individual location after that many result pages regardless of Max stations. 0 on either means no cap; on a whole state that can be thousands of billed rows, so start narrow and raise it once you know the size of the area. When locations overlap (for example a postal code and the city around it), a station one location read but left over once its share was full is not offered again by the next location, so such a run can return fewer than Max stations even though more matching stations exist; list areas that do not overlap, or raise Max stations.
Tip: with Incremental mode on, a small Max stations can under count a schedule. Max stations is shared out across your locations, and each location stops reading once it has gathered its share of matching stations. That share is counted before this actor knows which of those stations are actually new or changed (with no filter on, it even shrinks the page it asks the site for down to that share). If the stations it reads first turn out to be unchanged since the last run, they are correctly suppressed and not billed, but the location still stops there, even though Max stations was never reached. A genuinely new or changed station further down the same search is never read, the run can log "nothing changed since the last run", and because every scheduled run with the same input stops at the same place, that change can stay unseen run after run. This never creates false EXPIRED rows: a location stopped by its share is not treated as a complete scan, and a station that was read is never marked as disappeared. Workaround: for a scheduled Incremental campaign, set Max stations generously (comfortably above the number of stations you expect to track, or 0 for no limit) so the walk is never cut short before it reaches the whole tracked area.
Resume and recurring updates
- Resume from a previous run (
resumeFromRunId) continues one interrupted crawl: paste its run or dataset id and the stations it already returned are skipped, so you don't pay twice. It only bootstraps Incremental mode's saved state when that state is still empty (the first run for a search); pointed at a search that already has tracked history, the run fails with a clear message asking you to remove Resume or pick a different State key instead. - Incremental mode (
incrementalMode) is for scheduled runs over the same search. It remembers the previous run itself, so no id has to be pasted anywhere. Each station is classifiedNEW,UPDATED(withchangedFieldsnaming what changed),UNCHANGED(suppressed and not billed unless Also return unchanged stations is on),REAPPEARED, orEXPIRED(only after a run that completed a full scan of the search, and only with Also return stations that disappeared on).stateKeynames or shares the stored state; left empty, a key is derived from the mode, locations, links and every filter, so changing a filter starts a fresh baseline rather than silently mixing two searches.
Tip: EXPIRED rows never exceed the room left under Max stations. Also return stations that disappeared tombstones tracked stations that are no longer found, but only after a complete scan, and no more than Max stations minus the rows already returned in that run (with Max stations at 0 there is no limit). Stations beyond that stay tracked and are tombstoned by a later run, so a long gap or a lowered Max stations cannot bill one huge batch. With Expire after missed scans above 1, a station must be missing from that many complete scans in a row first; EXPIRED means the site stopped listing it in this search, not that it closed.
Tip: starting Incremental mode from Resume does not re-bill the resumed stations. When Resume bootstraps an empty Incremental state, the stations the earlier run already returned are saved by id only. On the next ordinary Incremental run each of them is recorded as UNCHANGED the first time it is seen again (and so not billed) and compared normally from then on. If one disappears before then it is skipped (and counted in the log) rather than emitted as an incomplete EXPIRED row. After an Apify platform migration that restarts an Incremental run midway, stations returned before the restart can come back once more on the next run.
priceTrend, offers, reviews and topSpotters never appear in changedFields. priceTrend is an area aggregate shared by every station in a metro and recomputed continuously, so tracking it would relabel a whole city as changed with no station level change behind it. offers carries a fresh opaque id on nearly every fetch for the same promotion. reviews and topSpotters are excluded on size, because a busy station's review corpus would not fit in the saved baseline. Nothing is lost: reviewCount, ratingsCount and starRating are all tracked, so a new review still shows up as an UPDATED station naming those fields.
Send results into your apps (MCP connectors)
Optionally pipe results into the apps you already use through Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed and nothing extra is charged.
- Authorize a connector once under Apify -> Settings -> Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the
mcpConnectorsfield. (If the picker is empty, you have not authorized a connector yet.) - For Notion, also set
notionParentPageUrlto the page where station entries should be created.
Each station becomes one entry with a title and its key fields as plain text; the complete record always stays in the Apify dataset. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (locations) or url (paste station or search links). Links or Stations given with no location of any kind always run in url mode. |
locations | array | (prefill 78701; search mode with no target of any kind searches 78701) | One entry per area: postal code, city and state, state, or latitude,longitude for a radius search. Search mode only. |
urls | array | one example of each link shape (see below) | Station, search or price-page links, or bare station ids. URL mode only. |
brands | array | (none) | Return only these brands, checked against GasBuddy's own brand list. |
brandNames | array | (none) | Alias of brands; both lists are merged. |
zipCodes | array | (none) | Alias for postal codes in locations (search mode). |
cityStates | array | (none) | Alias for City, ST entries in locations (search mode). |
coordinates | array | (none) | Objects with latitude and longitude; each is a radius search (search mode). |
stations | array | (none) | Alias for station ids or links in urls, read in url mode only: with Locations also given and Mode left on search, the run stays in search mode, the Stations are not looked up, and the run log says so. If you set Mode to url explicitly, the Stations are looked up and the Locations are not read. A station the site does not return is listed in the LOOKUP_STATUS key-value record, never as a dataset row, so it is not billed. |
streetAddresses | array | (none) | Full street addresses; used alone, only stations whose street line matches are kept (search mode). |
fuelType | string | regular | The grade GasBuddy ranks the area by; every grade a station sells is still returned. |
maxPriceAgeHours | string | 0 (any age) | Keep only stations whose price was confirmed within this many hours. |
maxReportAgeHours | integer | (none) | Keep only stations with a price reported within this many hours, any whole number. Used when Maximum price age is Any age. |
minPrice | integer | (none) | Keep stations whose cheapest reported price is at least this much. |
maxPrice | integer | (none) | Keep stations whose cheapest reported price is at most this much. |
minStarRating | integer | (any) | Keep only stations rated at least this many stars. |
amenities | array | (none) | Keep only stations that have ALL of the amenities you pick. |
openNowOnly | boolean | false | Keep only stations GasBuddy currently reports as open. |
withPricesOnly | boolean | false | Keep only stations with at least one reported price. |
maxDistanceMiles | integer | (none) | Keep only stations within this many miles of a coordinate search. |
fetchReviews | boolean | false | Add member reviews to each station; extra pages beyond the free first page are charged. |
maxReviewsPerStation | integer | 10 | Upper bound on review rows per station, at most 1000. Up to 10 come free with the search; asking for more fetches extra pages, which are charged once per station. 0 means no reviews. |
maxStations | integer | 20 | Stop after this many matching stations across all locations. 0 means no limit. |
maxPages | integer | 0 | Stop each location after this many result pages. 0 means walk to the end. |
resumeFromRunId | string | (none) | Continue one interrupted run: paste its run id or dataset id. |
incrementalMode | boolean | false | Return only new and changed stations on scheduled runs. |
stateKey | string | (none) | Name or share an Incremental mode state; derived from the search when left empty. |
emitUnchanged | boolean | false | Incremental mode only. Also return (and bill) unchanged stations. |
emitExpired | boolean | false | Incremental mode only. Also return (and bill) stations that disappeared. |
changeScope | string | all | Incremental mode only. priceAndStock compares only the lowest price, its grade, the price unit and the per-grade cash price, credit price and member discount. Switching it never re-bills stations. |
compactUpdates | boolean | false | Incremental mode only. UPDATED and EXPIRED rows are slim, with priceChanges and per fuel grade variantChanges (old and new values). NEW rows stay complete. |
expireAfterMissedScans | integer | 1 | Incremental mode only. Consecutive complete scans a station must be missing before it is marked EXPIRED. |
ignoreFieldsForChanges | array | (none) | Incremental mode only. Field names (dotted paths such as prices.creditReportedBy reach into the price list) that never count as a change; they stay in the output. |
proxy | object | Apify Proxy, country US | Connection settings; GasBuddy only answers from a North American exit. |
mcpConnectors | array | (none) | Optional. Send a condensed summary of each returned station to connectors you authorized under Apify Integrations (Notion, Linear, Airtable, Apify). The dataset is never changed and nothing extra is charged. |
notionParentPageUrl | string | (none) | Notion connector only: page under which station pages are created. |
maxNotifyListings | integer | 50 | Cap on stations exported per connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"stationId": "90210001","name": "Example Fuel Stop","url": "https://www.gasbuddy.com/station/90210001","brand": "Example Fuel Brand","brandId": "501","brandLogoUrl": "https://images.example.test/b/501.png","allBrands": ["Example Fuel Brand"],"addressLine1": "400 Sample Ave","addressLine2": null,"city": "Example City","state": "TX","postalCode": "00000","country": "US","latitude": 29.9012,"longitude": -95.4123,"phone": "555-010-0100","distance": "1.2mi","distanceMiles": 1.2,"openStatus": "open","openingHours": null,"nextOpenAt": null,"nextCloseAt": null,"amenities": ["C-Store", "Pay At Pump", "Air Pump"],"amenityIds": ["c_store", "pay_at_pump", "air"],"fuels": ["regular_gas", "midgrade_gas", "premium_gas", "diesel"],"prices": [{"fuelType": "regular","fuelProduct": "regular_gas","label": "Regular","cashPrice": null,"cashPostedAt": null,"cashReportedBy": null,"creditPrice": 3.29,"creditPostedAt": "2026-02-01T09:00:00.000Z","creditReportedBy": "sample-member","formattedPrice": "$3.29","memberDiscount": 0},{"fuelType": "diesel","fuelProduct": "diesel","label": "Diesel","cashPrice": null,"cashPostedAt": null,"cashReportedBy": null,"creditPrice": null,"creditPostedAt": null,"creditReportedBy": null,"formattedPrice": null,"memberDiscount": 0}],"lowestPrice": 3.29,"lowestPriceFuelType": "regular","lastPriceReportedAt": "2026-02-01T09:00:00.000Z","lastPriceReportedBy": "sample-member","priceUnit": "dollars_per_gallon","currency": "USD","starRating": 4.1,"ratingsCount": 56,"reviewCount": 12,"reviews": [{"reviewId": "sample-review-1","rating": 5,"text": "Sample review text.","reviewedAt": "2026-01-15T00:00:00.000Z","agreeCount": 2,"author": "sample-member"}],"topSpotters": [{ "name": "sample-member", "reportCount": 9, "streakDays": 3 }],"priceTrend": [{ "areaName": "Example City", "country": "US", "todayAverage": 3.35, "todayLow": 3.15, "direction": "falling" }],"offers": [],"cardPaymentAvailable": true,"emergencyStatus": null,"hasActiveOutage": false,"isTruckFleetSite": false,"isEnterprise": false,"searchTerm": "Example City, TX","searchLocation": "Example City, TX","scrapedAt": "2026-02-01T12:00:00Z"}
Plan Requirement
GasBuddy's access control is driven by geography and IP reputation: a request from outside North America (or from some hosting ranges) is refused with a challenge page. A US exit is therefore required, so keep the connection country set to US (the default); a US exit reaches the Canadian stations too, with prices in CAD per litre. The default is Apify Proxy with country US. We have not proven that every datacenter exit passes, so no run is guaranteed to need no action: if the site refuses the default exit, a residential connection gives more headroom, and you can pick it under Connection.
If the connection cannot be used, the run logs "The selected connection is unavailable to this run." followed by "Continuing on a backup connection; some pages may still be unavailable." If the account has no proxy access at all and no page can be read, the run ends with "The proxy connection was refused for this account, so no page could be read." If requests fail for another reason, it ends with "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."
FAQ
How much does it cost?
The run is billed with three events, shown on the Pricing tab: Station result (once per station row returned in the dataset), Extra review pages (once per station for which you asked for more review rows than the free first page carries; the first page never costs anything), and Actor start (once per GB of run memory, minimum one). Unchanged stations in Incremental mode, skipped stations and lookups that return no station are not billed. Use Max stations to cap the cost of any run.
Which views does the dataset have?
Besides the overview, the Prices view lists the cash and credit price per fuel grade, the Reviews view lists member reviews, and the Changes view lists changeType and changedFields of Incremental runs. A run summary is saved under the OUTPUT key of the key-value store.
Is it legal to scrape GasBuddy?
This actor collects only publicly available station and price data. You are responsible for how you use it: follow GasBuddy's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices are reported by GasBuddy's own members and change frequently; treat this data as a snapshot, not a guarantee of the price at the pump.
Can I get only new or changed prices on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and changed stations, and unchanged ones are not billed. See the caveat above about pairing a small Max stations with Incremental mode.
Why is brand sometimes null?
GasBuddy indexes independent, unbranded stations with an empty brand list, so brand comes back null for them, and a wide search will reach them. name is still always populated for those stations, so they are never silently dropped.
Why did my run fail instead of returning an empty dataset?
If the site refuses every request, the run stops with a clear message so "no stations found" is never confused with "nothing could be read". Run it again in a few minutes, with Apify Proxy enabled and the connection country set to United States. If only some requests were refused, you still get what was found.
Why did Incremental mode bill far more stations than Max stations in one run?
See the tip above under Resume and recurring updates: Also return stations that disappeared tombstones every tracked station a complete scan did not see, and that batch is not limited by Max stations. Keep it off until you are ready for a full reconciliation run.
Can I use it with AI agents or automation tools?
Yes. Call it from any Apify integration, or trigger it from Make, Zapier, n8n or a webhook through the Integrations tab.
๐ More scrapers you might like
Pair this actor with these related scrapers from the same team:
| โ๏ธ Fuel Price Scraper Scrape current petrol and fuel prices across Australia and New Zealand. Search by address... | ๐งฉ TuneIn Scraper Scrape TuneIn by keyword, category, genre, location or URL. Extract stations and podcasts... |
| ๐ Americanas Brazil Scrape Americanas Brazil (americanas.com.br) by keyword, department or pasted link... | ๐งฉ Suumo.jp Scraper Scrape Suumo, Japan's largest property portal: rental rooms with rent, management fees... |
| ๐ฅฆ REWE.de Scraper Scrape REWE Germany (rewe.de) grocery products: current price, was-price and discount... | ๐ CommercialGuru SG Property Listings & Agent Contacts Scraper Scrape commercialguru.com.sg Singapore listings at scale. Extract prices, PSF, floor... |
๐ Browse all abotapi scrapers
๐ฌ 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.