Python wrapper for GasBuddy GraphQL API
Project description
py-gasbuddy
Python async library for retrieving gas station data from the GasBuddy GraphQL API.
Provides three query methods covering single-station detail (prices, hours, amenities, offers), location-based station search, and a price-service query that returns prices plus regional trend data for nearby stations.
Installation
pip install py-gasbuddy
Quick Start
import asyncio
from py_gasbuddy import GasBuddy
async def main():
gb = GasBuddy(station_id=205033)
data = await gb.price_lookup()
print(data["name"], data["regular_gas"]["price"])
asyncio.run(main())
GasBuddy Class
GasBuddy(
station_id: int | None = None,
solver_url: str | None = None,
cache_file: str = "",
timeout: int = 60000,
session: aiohttp.ClientSession | None = None,
)
| Parameter | Description |
|---|---|
station_id |
GasBuddy station ID — required for price_lookup() |
solver_url |
URL of a FlareSolver instance for Cloudflare bypass |
cache_file |
Path for the CSRF-token cache file (default: py_gasbuddy/gasbuddy_cache) |
timeout |
Request timeout in milliseconds (default: 60000) |
session |
Optional aiohttp.ClientSession to reuse — caller manages lifecycle |
Cloudflare / FlareSolver
GasBuddy's homepage is protected by Cloudflare. The library extracts a CSRF token from the HTML before making GraphQL requests. If the direct fetch fails, pass a solver_url pointing to a running FlareSolver instance:
gb = GasBuddy(station_id=205033, solver_url="http://localhost:8191/v1")
Tokens are cached on disk to avoid redundant solver calls across restarts.
Methods
price_lookup() → StationPrice
Returns full detail for a single station including prices, hours, amenities, brand, and offers. Requires station_id to be set at construction time.
gb = GasBuddy(station_id=205033)
data = await gb.price_lookup()
# Station metadata
print(data["name"]) # "Wawa"
print(data["phone"]) # "610-555-0100"
print(data["open_status"]) # "open"
print(data["star_rating"]) # 4.2
print(data["ratings_count"]) # 38
print(data["pay_status"]) # True (GasBuddy Pay available)
print(data["fuels"]) # ["regular_gas", "midgrade_gas", "premium_gas"]
# Address
print(data["address"]["line1"]) # "123 Main St"
print(data["address"]["locality"]) # "Phoenixville"
print(data["address"]["region"]) # "PA"
# Brands
if data["brands"]:
print(data["brands"][0]["name"]) # "Shell"
print(data["brands"][0]["imageUrl"]) # logo URL
# Hours
print(data["hours"]["status"]) # "open"
print(data["hours"]["openingHours"]) # "24 Hours"
# Prices (credit and cash)
reg = data["regular_gas"]
print(reg["price"]) # 3.27 (credit price)
print(reg["cash_price"]) # 3.17 (None if not reported)
print(reg["formatted_price"]) # "$3.27"
print(reg["last_updated"]) # "2024-09-06T09:54:05Z"
print(reg["credit"]) # reporter nickname
# Emergency status (fuel/power outage reports)
print(data["emergency_status"]["hasGas"]["reportStatus"]) # None or "has_gas"
# Loyalty offers
for offer in data["offers"]:
print(offer["id"], offer["types"], offer["use"])
# Amenities
for amenity in data["amenities"]:
print(amenity["name"]) # "ATM", "Restrooms", etc.
Fuel product keys are dynamic and match whatever the station reports:
regular_gas, midgrade_gas, premium_gas, diesel, e85, e15, etc.
location_search(lat, lon, zipcode, brand_id, fuel, cursor) → LocationSearchResult
Returns a dict with results (list of StationSummary) and optionally next_cursor for pagination. Pass either lat+lon or zipcode.
# By ZIP code
result = await GasBuddy().location_search(zipcode=85396)
# By GPS coordinates
result = await GasBuddy().location_search(lat=33.465, lon=-112.505)
# Filter to diesel-only stations (fuel ID 4)
result = await GasBuddy().location_search(zipcode=85396, fuel=4)
# Filter to a specific brand (38 = Costco)
result = await GasBuddy().location_search(zipcode=85396, brand_id=38)
for s in result["results"]:
print(s["station_id"], s["name"], s["distance"])
print(s["address"]["locality"], s["address"]["region"])
print(s["fuels"])
# Paginate
cursor = result.get("next_cursor")
if cursor:
page2 = await GasBuddy().location_search(zipcode=85396, cursor=cursor)
StationSummary fields:
| Field | Type | Description |
|---|---|---|
station_id |
str |
GasBuddy station identifier |
name |
str |
Station/brand display name |
address |
Address |
line1, line2, locality, region, postalCode, country |
brands |
list[Brand] |
Brand name, brandId, logo imageUrl |
distance |
float | None |
Distance from search point (miles) |
star_rating |
float | None |
Community star rating |
ratings_count |
int | None |
Number of ratings |
fuels |
list[str] |
Fuel products sold |
price_unit |
str | None |
"dollars_per_gallon" or "cents_per_liter" |
price_lookup_service(lat, lon, zipcode, limit, brand_id, fuel, cursor) → PriceServiceResult
Returns prices for nearby stations plus regional trend data. Pass either lat+lon or zipcode. limit defaults to 5.
result = await GasBuddy().price_lookup_service(zipcode=85396, limit=10)
for station in result["results"]:
print(station["name"], station["distance"])
if "regular_gas" in station:
print(station["regular_gas"]["price"])
print(station["regular_gas"]["formatted_price"])
# Regional trends
for trend in result.get("trend", []):
print(trend["area"], trend["average_price"], trend["lowest_price"])
# Paginate
cursor = result.get("next_cursor")
if cursor:
page2 = await GasBuddy().price_lookup_service(zipcode=85396, cursor=cursor)
Results have the same structure as StationPrice (see price_lookup above) minus station-only fields like amenities, hours, offers, and pay_status.
Fuel Types
| Filter ID | Product key | Description |
|---|---|---|
| 1 | regular_gas |
Regular (85-87 Octane) |
| 2 | midgrade_gas |
Mid-Grade (89 Octane) |
| 3 | premium_gas |
Premium (91-93 Octane) |
| 4 | diesel |
Diesel |
| 5 | e85 |
E85 |
| 12 | unl88 |
Unleaded 88 (E15) |
The filter ID is the integer passed to the fuel parameter on location_search and price_lookup_service. The product key is the string used as a dictionary key in StationPrice results (e.g. data["regular_gas"]). Both are also available as FUEL_FILTER_IDS and FUEL_PRODUCTS in py_gasbuddy.consts.
Data Models
All return types are TypedDict subclasses defined in py_gasbuddy.models.
PriceNode
class PriceNode(TypedDict):
credit: str | None # reporter nickname
price: float | None # credit price (None = not reported)
cash_price: float | None # cash price (None = not reported)
last_updated: str | None # ISO 8601 timestamp
formatted_price: str | None # "$3.27" or "131.9¢"
deal_price: float | None # price after GasBuddy Pay discount (None if no offer or no price)
deal_price is computed from the pwgbDiscount field returned by the GasBuddy GraphQL API, which covers the Flash Deal and GasBuddy+ Member Savings components. The Card Savings Boost (available to GasBuddy+ members who select Fleet Card at the pump) is sourced from the mobile REST API and is not included here, so deal_price may be slightly higher than what the GasBuddy app displays for GasBuddy+ members.
TrendData
class TrendData(TypedDict):
area: str # "Arizona" or "United States"
average_price: float # today's average
lowest_price: float # today's lowest (0 if not available)
Error Handling
| Exception | Raised when |
|---|---|
MissingSearchData |
location_search or price_lookup_service called without coordinates or ZIP |
LibraryError |
Network or parse error; check logs for details |
APIError |
GasBuddy's GraphQL returned an errors field |
from py_gasbuddy.exceptions import APIError, LibraryError, MissingSearchData
try:
data = await gb.price_lookup()
except LibraryError:
print("Network or token error")
except APIError:
print("GasBuddy API error")
Cache Management
# Clear the cached CSRF token (forces a fresh fetch on next request)
await gb.clear_cache()
Examples
See the examples/ directory for runnable scripts:
| Script | Description |
|---|---|
price-lookup.py |
Full station detail by station ID |
price-lookup-service.py |
Nearby prices + trends by ZIP or GPS |
location-search.py |
Station list (no prices) by ZIP or GPS |
ev-chargers.py |
EV charger locations and status (CLI) |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_gasbuddy-0.6.0.tar.gz.
File metadata
- Download URL: py_gasbuddy-0.6.0.tar.gz
- Upload date:
- Size: 164.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69e698968fd536acaa0a5e3114267afba2acdbbb9138a7a8a03c30a65416e7a5
|
|
| MD5 |
575e7fbd086d6c8608d8b1c36a6442e5
|
|
| BLAKE2b-256 |
41e6624828fed4c6a4a8a87d9824cb521a825a9536b1b50e01aa828a2c9eb346
|
Provenance
The following attestation bundles were made for py_gasbuddy-0.6.0.tar.gz:
Publisher:
publish-to-pypi.yml on firstof9/py-gasbuddy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_gasbuddy-0.6.0.tar.gz -
Subject digest:
69e698968fd536acaa0a5e3114267afba2acdbbb9138a7a8a03c30a65416e7a5 - Sigstore transparency entry: 1631583050
- Sigstore integration time:
-
Permalink:
firstof9/py-gasbuddy@7e9981e7aee6a22c2ee67c037dd21511f6922588 -
Branch / Tag:
refs/tags/0.6.0 - Owner: https://github.com/firstof9
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@7e9981e7aee6a22c2ee67c037dd21511f6922588 -
Trigger Event:
release
-
Statement type:
File details
Details for the file py_gasbuddy-0.6.0-py3-none-any.whl.
File metadata
- Download URL: py_gasbuddy-0.6.0-py3-none-any.whl
- Upload date:
- Size: 18.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cdd3dd8452127d9c2876290afb171de814c3d6538ba5b59899b4b7c5c74795a
|
|
| MD5 |
3fec12c8f83ec4a14bbe05db9f85e702
|
|
| BLAKE2b-256 |
8f80e6da75b2339722a720f8379744aa653a209861ddb2b87c4b73daf1343287
|
Provenance
The following attestation bundles were made for py_gasbuddy-0.6.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on firstof9/py-gasbuddy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_gasbuddy-0.6.0-py3-none-any.whl -
Subject digest:
1cdd3dd8452127d9c2876290afb171de814c3d6538ba5b59899b4b7c5c74795a - Sigstore transparency entry: 1631583052
- Sigstore integration time:
-
Permalink:
firstof9/py-gasbuddy@7e9981e7aee6a22c2ee67c037dd21511f6922588 -
Branch / Tag:
refs/tags/0.6.0 - Owner: https://github.com/firstof9
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@7e9981e7aee6a22c2ee67c037dd21511f6922588 -
Trigger Event:
release
-
Statement type: