Skill: gustavoziaugra/gas-price-alert (version 1.1.0)

--- name: gas-price-alert description: Find and monitor gas prices with daily notifications. Use when searching for the cheapest gas in a specific area, tracking Costco and other discount fuel stations, or setting up daily gas price alerts. Supports any US location with configurable radius and fuel type. --- # Gas Price Alert ## Overview Automatically search for the cheapest gas prices in your area, with a focus on Costco and other discount stations. Get daily notifications with the best options within a specified radius. ## Quick Start 1. **Configure location** - Set your city/coordinates and search radius 2. **Run search** - Find gas stations and estimated prices 3. **Schedule daily alerts** - Get morning notifications with cheapest options 4. **Focus on Costco** - Costco typically has gas $0.15-0.25 below market average ## Workflow ### Step 1: Configure Your Location **Option A: Use ZIP code (recommended)** ```bash # Search by ZIP code python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --summary ``` **Option B: Use coordinates** Default locations are pre-configured for Columbus, Ohio: ```bash # Columbus, OH (downtown) lat: 39.9612 lon: -82.9988 radius: 20 miles ``` To use a different location: ```bash python3 scripts/gas_alternative.py --lat <latitude> --lon <longitude> --radius <miles> ``` **Common US cities:** - Columbus, OH: 39.9612, -82.9988 - Chicago, IL: 41.8781, -87.6298 - New York, NY: 40.7128, -74.0060 - Los Angeles, CA: 34.0522, -118.2437 - Miami, FL: 25.7617, -80.1918 ### Step 2: Search for Gas Stations ```bash # Search with summary output python3 scripts/gas_alternative.py --lat 39.9612 --lon -82.9988 --radius 20 --fuel 87 --summary # Save to file python3 scripts/gas_alternative.py --lat 39.9612 --lon -82.9988 --radius 20 --fuel 87 --output gas_prices.json ``` **Parameters:** - `--zip`: ZIP code (overrides lat/lon, e.g., `--zip 43215`) - `--lat`: Latitude (default: 39.9612 - Columbus, OH) - `--lon`: Longitude (default: -82.9988 - Columbus, OH) - `--radius`: Search radius in miles (default: 20) - `--fuel`: Fuel type - 87, 89, 91, diesel (default: 87) - `--base-price`: Base price for estimation (default: 2.89) - `--output`: Output file (default: gas_prices.json) - `--summary`: Print human-readable summary to stdout ### Step 3: Set Up Daily Alerts Use OpenClaw cron to receive daily morning notifications: ```json { "name": "Gas price alert", "schedule": { "kind": "cron", "expr": "0 8 * * *", "tz": "America/New_York" }, "payload": { "kind": "agentTurn", "message": "Get me gas prices for Columbus, OH this morning. Focus on Costco and show the cheapest 87 octane within 20 miles of downtown." }, "sessionTarget": "main" } ``` This runs every day at 8 AM Eastern Time. ### Step 4: Receive Notifications The agent will: 1. Search for gas stations in your area 2. Identify Costco and discount stations 3. Generate a summary with the cheapest options 4. Send the summary via Telegram **Example notification:** ``` ⛽ Gas Prices (87 Octane) - Columbus, OH 🏠 Costco (Typically Cheapest) • Costco Gas 💰 $2.69 (est.) 📍 5000 Morse Rd, Columbus, OH 43213 (7.9 miles from downtown) 💡 Tip: Costco typically has gas $0.15-0.25 below market average. ``` ## Output Format Each station includes: ```json { "source": "osm", "name": "Costco Gas", "brand": "Costco", "address": "5000 Morse Rd, Columbus, OH 43213", "lat": 39.9667, "lon": -82.8500, "distance": 7.9, "fuel_type": "87", "price": 2.69, "price_text": "$2.69 (est.)", "is_costco": true, "scraped_at": "2026-02-10T21:00:00.000Z" } ``` ## How It Works 1. **OpenStreetMap/Overpass API** - Finds all gas stations in the area 2. **Costco database** - Known Costco locations are matched and prioritized 3. **Price estimation** - Costco prices estimated $0.15-0.25 below market average 4. **Distance calculation** - Uses geodesic distance for accurate mileage 5. **Smart filtering** - Removes duplicates and sorts by relevance ## Limitations - **Real-time prices:** Currently uses estimated prices for Costco. For exact prices, check GasBuddy.com or station apps. - **Coverage:** Relies on OpenStreetMap data completeness - **Estimation accuracy:** Costco prices estimated based on typical discount patterns ## For Real-Time Prices To get actual real-time prices: 1. **GasBuddy.com** - Check manually or use their commercial API 2. **Station apps** - Costco, Kroger, Shell, etc., have apps with current prices 3. **AAA** - Provides average prices by region 4. **Waze** - Community-sourced price updates ## Troubleshooting ### No stations found - Increase the `--radius` parameter - Verify coordinates are correct - Check if the area has good OpenStreetMap coverage ### Incorrect prices - Prices for non-Costco stations are estimated as "N/A" - Costco prices are estimates based on typical discount patterns - For exact prices, use GasBuddy or the station's app ### Geopy not installed ```bash pip install geopy ``` ## Resources ### scripts/gas_alternative.py Main script for searching gas stations using OpenStreetMap and Overpass API. **Features:** - Finds all gas stations within radius - Identifies Costco locations - Estimates Costco prices - Calculates distances - Generates human-readable summaries ### scripts/gasbuddy_search.py Alternative script for GasBuddy integration (requires Playwright or API key). **Use when:** - You have a GasBuddy API key - You need real-time prices - You're willing to use Playwright for JavaScript rendering ### references/locations.md Coordinates and configurations for common US cities. ## Dependencies Install required packages: ```bash pip install requests geopy ``` For Playwright-based GasBuddy scraping (optional): ```bash pip install playwright playwright install ```

path: README.md
content: # Gas Price Alert / Alerta de Preço de Combustível

**⛽ Find cheapest gas prices with daily notifications / Encontre os preços mais baratos de combustível com alertas diários**

This OpenClaw skill helps you find and monitor gas prices, with special focus on Costco and other discount stations.

Este skill do OpenClaw ajuda você a encontrar e monitorar preços de combustível, com foco especial em Costco e outros postos com desconto.

## Features / Funcionalidades

- 🔍 **ZIP code search** - Search by any US ZIP code / Busca por qualquer CEP dos EUA
- 💰 **Best price detection** - Shows cheapest options first / Mostra opções mais baratas primeiro
- 📏 **Configurable radius** - Set your preferred search range / Defina o raio de busca preferido
- 🏠 **Costco support** - Special tracking of Costco locations / Rastreamento especial de locais da Costco
- ⏰ **Daily alerts** - Schedule automated notifications / Agende notificações automatizadas
- 🌍 **Works anywhere** - Supports any US location / Funciona em qualquer lugar dos EUA

## Quick Start / Início Rápido

### English

```bash
# Search by ZIP code
python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --summary

# Save to file
python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --output gas_prices.json
```

### Português

```bash
# Buscar por CEP
python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --summary

# Salvar em arquivo
python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --output gas_prices.json
```

## Installation / Instalação

1. **Clone this repository / Clone este repositório:**

```bash
git clone https://github.com/GustavoZiaugra/gas-price-alert-skill.git
cd gas-price-alert-skill
```

2. **Install dependencies / Instale dependências:**

```bash
pip install requests geopy
```

3. **Load skill into OpenClaw / Carregar skill no OpenClaw:**
   - Open OpenClaw Control UI
   - Go to Skills → Import Skill
   - Select this directory

## Usage Examples / Exemplos de Uso

### By ZIP Code / Por CEP (Recommended)

```bash
# Columbus, OH
python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --summary

# Miami, FL
python3 scripts/gas_alternative.py --zip 33101 --radius 15 --fuel 87 --summary

# Los Angeles, CA
python3 scripts/gas_alternative.py --zip 90210 --radius 10 --fuel 91 --summary
```

### Por Coordenadas (Português)

```bash
# Buscar por coordenadas
python3 scripts/gas_alternative.py --lat 39.9612 --lon -82.9988 --radius 20 --fuel 87 --summary
```

### Daily Alerts / Alertas Diários

Set up automated daily alerts using OpenClaw's cron system / Configure alertas diárias automatizadas usando o sistema cron do OpenClaw:

```json
{
  "name": "Gas price alert",
  "schedule": {
    "kind": "cron",
    "expr": "0 8 * * *",
    "tz": "America/New_York"
  },
  "payload": {
    "kind": "systemEvent",
    "text": "⛽ Gas Prices / Preços de Combustível\n\nRun: python3 scripts/gas_alternative.py --zip 43215 --radius 20 --fuel 87 --summary"
  },
  "sessionTarget": "main"
}
```

## Parameters / Parâmetros

| Parameter | Description | Descrição | Default | Exemplo |
|-----------|-------------|-------------|----------|---------|
| `--zip` | ZIP code for location / CEP para localização | none | `--zip 43215` |
| `--lat` | Latitude / Latitude | 39.9612 | `--lat 40.7128` |
| `--lon` | Longitude / Longitude | -82.9988 | `--lon -74.0060` |
| `--radius` | Search radius in miles / Raio de busca em milhas | 20 | `--radius 15` |
| `--fuel` | Fuel type / Tipo de combustível (87, 89, 91, diesel) | 87 | `--fuel 91` |
| `--base-price` | Base price for estimation / Preço base para estimativa | 2.89 | `--base-price 3.15` |
| `--output` | Output file / Arquivo de saída | gas_prices.json | `--output results.json` |
| `--summary` | Print human-readable summary / Imprimir resumo legível | false | `--summary` |

## Example Output / Exemplo de Saída

```
⛽ Gas Prices (87 Octane) - Columbus, OH
⛽ Preços de Gasolina (87 Octanos) - Columbus, OH

💰 Best Prices Available (3 with prices)
💰 Melhores Preços Disponíveis (3 com preços)

• Costco Gas - $2.69 (est.)
  📍 5000 Morse Rd, Columbus, OH 43213 (7.9 miles) ⭐

• Costco Gas - $2.69 (est.)
  📍 1350 Hilliard Rome Rd, Columbus, OH 43228 (8.2 miles) ⭐

📍 Nearest Stations (Top 10 by distance)
📍 Postos Mais Próximos (Top 10 por distância)

1. Speedway (1.6 miles)
2. GetGo (2.2 miles)
3. Sunoco (2.3 miles)
4. Shell (2.8 miles)
5. Circle K (2.8 miles)

... and 129 more stations within 20 miles
... e 129 postos a mais dentro de 20 milhas

💡 Tips / Dicas:
• Costco typically has gas $0.15-0.25 below market average
• Costco geralmente tem gasolina $0.15-0.25 abaixo da média do mercado
• For exact prices, check GasBuddy.com or station's app
• Para preços exatos, verifique GasBuddy.com ou o app do posto
• Total stations found: 137
• Total de postos encontrados: 137
```

## How It Works / Como Funciona

1. **Geocoding:** Converts ZIP code to precise coordinates / Converte CEP para coordenadas precisas
2. **OpenStreetMap Search:** Finds all gas stations within radius / Encontra todos os postos de gasolina dentro do raio
3. **Costco Detection:** Identifies Costco stations (typically cheapest) / Identifica postos Costco (tipicamente mais baratos)
4. **Price Estimation:** Estimates Costco prices based on market averages / Estima preços da Costco baseado em médias de mercado
5. **Distance Calculation:** Uses geodesic distance for accurate mileage / Usa distância geodésica para milhas precisas
6. **Smart Filtering:** Removes duplicates and sorts by relevance / Remove duplicatas e ordena por relevância

## Limitations / Limitações

- **Real-time prices:** Uses estimated prices for Costco. For exact prices, check GasBuddy.com or station apps.
- **Preços em tempo real:** Usa preços estimados para Costco. Para preços exatos, verifique GasBuddy.com ou apps dos postos.
- **API availability:** OpenStreetMap/Overpass API may experience timeouts (try again later).
- **Disponibilidade de API:** API do OpenStreetMap/Overpass pode ter timeouts (tente mais tarde).
- **Coverage quality:** Depends on OpenStreetMap data completeness for your area.
- **Qualidade de cobertura:** Depende da completude dos dados do OpenStreetMap para sua área.

## Use Cases / Casos de Uso

### Daily Commuter / Deslocamento Diário
Check gas prices before your morning commute to find the cheapest station on your route.
Verifique preços de gasolina antes de seu deslocamento matinal para encontrar o posto mais barato na sua rota.

### Road Trip / Viagem
Find cheap gas along your route when traveling to a new city.
Encontre gasolina barata ao longo de sua rota quando viajar para uma nova cidade.

### Costco Shopper / Cliente Costco
Costco members can find the best time to fill up while shopping for groceries.
Membros da Costco podem encontrar o melhor momento para abastecer enquanto compram mantimentos.

## Contributing / Contribuindo

To add more Costco locations or improve the skill:
Para adicionar mais locais da Costco ou melhorar a skill:

1. Edit `scripts/gas_alternative.py`
2. Add locations to `search_costco_locations()` / Adicionar locais na função `search_costco_locations()`
3. Submit a pull request / Envie um pull request

## License / Licença

MIT License - Use freely for personal and commercial purposes.
Licença MIT - Use livremente para fins pessoais e comerciais.

## Credits / Créditos

Created by **Gustavo (GustavoZiaugra)** with OpenClaw
Criado por **Gustavo (GustavoZiaugra)** com OpenClaw

- OpenStreetMap for station data / OpenStreetMap para dados de postos
- Geopy for geocoding / Geopy para geocodificação
- Requests for HTTP handling / Requests para manipulação HTTP

---

**Find this and more OpenClaw skills at ClawHub.com**
**Encontre este e mais skills do OpenClaw em ClawHub.com**

⭐ **Star this repository if you find it useful!**
**⭐ Dê uma estrela neste repositório se você achar útil!**

🇧🇷 **Disponível em português e inglês!**


path: references/locations.md
content: # Gas Price Alert - US City Coordinates

## Common US Cities

Use these coordinates for `--lat` and `--lon` parameters.

### Midwest

**Columbus, OH**
```
lat: 39.9612
lon: -82.9988
downtown: 39.9612, -82.9988
airport (CMH): 39.9980, -82.8919
```

**Chicago, IL**
```
lat: 41.8781
lon: -87.6298
downtown: 41.8781, -87.6298
airport (ORD): 41.9742, -87.9073
```

**Indianapolis, IN**
```
lat: 39.7684
lon: -86.1581
downtown: 39.7684, -86.1581
airport (IND): 39.7173, -86.2944
```

**Detroit, MI**
```
lat: 42.3314
lon: -83.0458
downtown: 42.3314, -83.0458
airport (DTW): 42.2162, -83.3554
```

### East Coast

**New York, NY**
```
lat: 40.7128
lon: -74.0060
downtown: 40.7128, -74.0060
airport (JFK): 40.6413, -73.7781
airport (LGA): 40.7769, -73.8740
```

**Boston, MA**
```
lat: 42.3601
lon: -71.0589
downtown: 42.3601, -71.0589
airport (BOS): 42.3656, -71.0096
```

**Philadelphia, PA**
```
lat: 39.9526
lon: -75.1652
downtown: 39.9526, -75.1652
airport (PHL): 39.8729, -75.2437
```

**Washington, DC**
```
lat: 38.9072
lon: -77.0369
downtown: 38.9072, -77.0369
airport (DCA): 38.8512, -77.0402
```

**Miami, FL**
```
lat: 25.7617
lon: -80.1918
downtown: 25.7617, -80.1918
airport (MIA): 25.7959, -80.2870
```

### West Coast

**Los Angeles, CA**
```
lat: 34.0522
lon: -118.2437
downtown: 34.0522, -118.2437
airport (LAX): 33.9425, -118.4081
```

**San Francisco, CA**
```
lat: 37.7749
lon: -122.4194
downtown: 37.7749, -122.4194
airport (SFO): 37.6213, -122.3790
```

**Seattle, WA**
```
lat: 47.6062
lon: -122.3321
downtown: 47.6062, -122.3321
airport (SEA): 47.4502, -122.3088
```

**Portland, OR**
```
lat: 45.5152
lon: -122.6784
downtown: 45.5152, -122.6784
airport (PDX): 45.5898, -122.5951
```

### South

**Atlanta, GA**
```
lat: 33.4484
lon: -84.3917
downtown: 33.4484, -84.3917
airport (ATL): 33.6407, -84.4277
```

**Dallas, TX**
```
lat: 32.7767
lon: -96.7970
downtown: 32.7767, -96.7970
airport (DFW): 32.8998, -97.0403
```

**Houston, TX**
```
lat: 29.7604
lon: -95.3698
downtown: 29.7604, -95.3698
airport (IAH): 29.9902, -95.3368
```

**Nashville, TN**
```
lat: 36.1627
lon: -86.7816
downtown: 36.1627, -86.7816
airport (BNA): 36.1263, -86.6774
```

### Mountain

**Denver, CO**
```
lat: 39.7392
lon: -104.9903
downtown: 39.7392, -104.9903
airport (DEN): 39.8561, -104.6737
```

**Phoenix, AZ**
```
lat: 33.4484
lon: -112.0740
downtown: 33.4484, -112.0740
airport (PHX): 33.4373, -112.0078
```

**Salt Lake City, UT**
```
lat: 40.7608
lon: -111.8910
downtown: 40.7608, -111.8910
airport (SLC): 40.7899, -111.9791
```

## Finding Coordinates

To find coordinates for any location:

1. **Google Maps:** Right-click on a location, coordinates appear
2. **OpenStreetMap:** Click on a location, coordinates shown in sidebar
3. **Geocoding services:** Nominatim (OpenStreetMap), Google Geocoding API

## Recommended Radius by Area Type

- **Urban downtown:** 5-10 miles (many stations)
- **Suburban:** 15-20 miles (good balance)
- **Rural:** 30-50 miles (fewer stations, need wider search)

## Costco Locations

The skill includes known Costco locations for major cities. For your area, you can:
1. Use the Costco Store Locator: https://www.costco.com/warehouse-locations
2. Add coordinates to the script's `search_costco_locations()` function
3. Or use the OpenStreetMap search which will find all stations including Costco


path: references/zip_codes.md
content: # Columbus, OH Area ZIP Codes

## Downtown & Central
- 43085 - Downtown Columbus
- 43201 - Columbus (Downtown)
- 43202 - Columbus (Downtown/OSU area)
- 43203 - Columbus (Victorian Village)
- 43204 - Columbus (German Village)
- 43205 - Columbus (Olde Towne East)
- 43206 - Columbus (Bexley area)
- 43209 - Columbus (Eastgate)
- 43210 - Columbus (Campus area)
- 43211 - Columbus (Northland)
- 43212 - Columbus (Morse Rd area)
- 43213 - Columbus (Eastland area)
- 43214 - Columbus (Hilltop)
- 43215 - Columbus (South Columbus)
- 43216 - Columbus (Hilltop area)
- 43217 - Columbus (Westland)
- 43218 - Columbus (Linden)
- 43219 - Columbus (Morse Rd)
- 43220 - Columbus (Airport area)
- 43221 - Columbus (Reynoldsburg)
- 43222 - Columbus (Eastmoor)
- 43223 - Columbus (Obetz area)
- 43224 - Columbus (Southfield)
- 43225 - Columbus (Gahanna area)
- 43226 - Columbus (Westgate)
- 43227 - Columbus (Whitehall area)
- 43228 - Columbus (Hilliard)
- 43229 - Columbus (Groveport)
- 43230 - Columbus (Brice area)
- 43231 - Columbus (Pickerington area)
- 43232 - Columbus (Canal Winchester)
- 43235 - Columbus (Southeast)
- 43601 - Toledo (for reference)
- 45039 - Grove City

## Coordinates for ZIP Centers

For accurate geocoding, use ZIP + city + state:
- "43215, Columbus, OH" - South Columbus
- "43213, Columbus, OH" - Morse Rd area
- "43228, Columbus, OH" - Hilliard

## How to Geocode ZIP Codes

Using Nominatim (OpenStreetMap):
```python
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent='gas-finder')
location = geolocator.geocode('43215, Columbus, OH')
```

Using Google Geocoding API (requires API key):
```python
import googlemaps
gmaps = googlemaps.Client(key='YOUR_API_KEY')
geocode_result = gmaps.geocode('43215')
```

Note: OpenStreetMap's Nominatim is free but has rate limits and may not be as accurate for ZIP-only searches. Always include city and state for best results.


path: scripts/gas_alternative.py
content: #!/usr/bin/env python3
"""
Alternative gas price search using free APIs and public data sources.
Usage: python3 gas_alternative.py --city "Columbus" --state "OH" --radius 20 --output results.json
"""

import argparse
import json
import math
import sys
from datetime import datetime, timezone

try:
    import requests
    from geopy.geocoders import Nominatim
    from geopy.distance import geodesic
except ImportError:
    print("Error: Install required packages: pip install requests geopy")
    sys.exit(1)

# Coordinates for downtown Columbus, OH
DOWNTOWN_COLUMBUS = (39.9612, -82.9988)

def get_distance(lat, lon, reference=DOWNTOWN_COLUMBUS):
    """Calculate distance from reference point in miles."""
    station_coords = (lat, lon)
    return geodesic(reference, station_coords).miles

def search_costco_locations(lat, lon, radius):
    """
    Search for Costco gas stations (Costco typically has cheap gas).

    Costco locations are available via various APIs, but for this example,
    we'll use a known list of Columbus area Costco locations.
    """
    # Known Costco locations in Columbus, OH area
    costco_stations = [
        {
            'name': 'Costco Gas',
            'address': '5000 Morse Rd, Columbus, OH 43213',
            'lat': 39.9667,
            'lon': -82.8500
        },
        {
            'name': 'Costco Gas',
            'address': '1350 Hilliard Rome Rd, Columbus, OH 43228',
            'lat': 39.9400,
            'lon': -83.1500
        },
        {
            'name': 'Costco Gas',
            'address': '8570 S Old State Rd, Lewis Center, OH 43035',
            'lat': 40.1800,
            'lon': -83.0000
        }
    ]

    # Filter by radius
    nearby = []
    for station in costco_stations:
        distance = geodesic((lat, lon), (station['lat'], station['lon'])).miles
        if distance <= radius:
            station['distance'] = distance
            nearby.append(station)

    return nearby

def search_gas_stations(lat, lon, radius, fuel_type='87'):
    """
    Search for gas stations using various free APIs.

    This uses the OpenStreetMap/Overpass API to find gas stations.
    """
    # Overpass API query for gas stations
    overpass_url = "http://overpass-api.de/api/interpreter"

    # Calculate bounding box
    # Approximate: 1 degree ≈ 69 miles
    delta_lat = radius / 69
    delta_lon = radius / (69 * math.cos(math.radians(lat)))

    bbox = f"{lat-delta_lat},{lon-delta_lon},{lat+delta_lat},{lon+delta_lon}"

    query = f"""
    [out:json][timeout:25];
    (
      node["amenity"="fuel"]({bbox});
      way["amenity"="fuel"]({bbox});
      relation["amenity"="fuel"]({bbox});
    );
    out body center;
    """

    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        }

        response = requests.post(overpass_url, data=query, headers=headers, timeout=30)
        response.raise_for_status()

        data = response.json()

        stations = []
        for element in data.get('elements', []):
            if 'lat' in element and 'lon' in element:
                tags = element.get('tags', {})

                # Calculate distance from downtown
                distance = get_distance(element['lat'], element['lon'])

                if distance <= radius:
                    station = {
                        'source': 'osm',
                        'name': tags.get('name', tags.get('brand', 'Unknown Station')),
                        'brand': tags.get('brand', 'Unknown'),
                        'address': tags.get('addr:full', ''),
                        'lat': element['lat'],
                        'lon': element['lon'],
                        'distance': round(distance, 2),
                        'fuel_type': fuel_type,
                        'price': 0,  # OSM doesn't have real-time prices
                        'price_text': 'N/A',
                        'scraped_at': datetime.now(timezone.utc).isoformat()
                    }
                    stations.append(station)

        print(f"Found {len(stations)} gas stations within {radius} miles")
        return stations

    except Exception as e:
        print(f"Error searching gas stations: {e}")
        return []

def estimate_costco_prices(stations, base_price=2.89):
    """
    Estimate Costco gas prices (typically $0.10-0.30 below average).

    In real usage, this would fetch actual prices from GasBuddy or similar.
    """
    for station in stations:
        if 'costco' in station['name'].lower():
            # Costco typically $0.15-0.25 below market average
            discount = 0.20
            estimated_price = round(base_price - discount, 2)
            station['price'] = estimated_price
            station['price_text'] = f"${estimated_price:.2f} (est.)"
            station['is_costco'] = True
        else:
            station['is_costco'] = False

    return stations

def generate_summary(stations, fuel_type='87'):
    """Generate a summary of gas prices."""
    if not stations:
        return "No gas stations found."

    # Sort by distance
    by_distance = sorted(stations, key=lambda x: x.get('distance', float('inf')))

    # Find Costco stations (typically cheapest)
    costco_stations = [s for s in stations if s.get('is_costco', False)]

    # Find stations with prices
    stations_with_prices = [s for s in stations if s.get('price', 0) > 0]
    by_price = sorted(stations_with_prices, key=lambda x: x.get('price', float('inf')))

    summary = f"⛽ Gas Prices ({fuel_type} Octane) - Columbus, OH"

    # Show cheapest available prices (Costco + others with prices)
    if by_price:
        summary += f"\n\n💰 **Best Prices Available** ({len(by_price)} with prices)\n"
        for station in by_price[:5]:
            summary += f"• {station['name']} ({station.get('brand', 'N/A')})\n"
            summary += f"  💰 {station.get('price_text', 'Check site')}\n"
            summary += f"  📍 {station['address']} ({station.get('distance', 0):.1f} miles)\n"
            if station.get('is_costco'):
                summary += f"  ⭐ Costco!\n"
            summary += "\n"
    elif costco_stations:
        # No prices except Costco estimates
        summary += f"\n\n💰 **Estimated Prices**\n"
        for station in costco_stations[:3]:
            summary += f"• {station['name']}\n"
            summary += f"  💰 {station.get('price_text', 'Check site')}\n"
            summary += f"  📍 {station['address']} ({station.get('distance', 0):.1f} miles)\n\n"

    # Show nearest stations
    summary += f"\n📍 **Nearest Stations** (Top 10 by distance)\n"
    for i, station in enumerate(by_distance[:10], 1):
        summary += f"{i}. {station['name']} ({station.get('brand', 'N/A')})\n"
        summary += f"   📍 {station['address'] or 'Address unknown'} ({station.get('distance', 0):.1f} miles)\n"
        summary += f"   💰 {station.get('price_text', 'Check for price')}\n"
        if station.get('is_costco'):
            summary += f"   ⭐ Costco!\n"
        summary += "\n"

    summary += f"... and {len(by_distance) - 10} more stations within 20 miles\n"

    summary += "\n💡 **Tips:**\n"
    summary += "• Costco typically has gas $0.15-0.25 below market average\n"
    summary += "• For exact prices, check GasBuddy.com or station's app\n"
    summary += f"• Total stations found: {len(by_distance)}"

    return summary

def save_stations(stations, output_file):
    """Save stations to JSON file."""
    with open(output_file, 'w') as f:
        json.dump(stations, f, indent=2, ensure_ascii=False)
    print(f"Saved {len(stations)} stations to {output_file}")

def geocode_zip(zip_code, city='Columbus', state='OH'):
    """Geocode ZIP code to lat/lon."""
    try:
        from geopy.geocoders import Nominatim
        geolocator = Nominatim(user_agent='openclaw-gasfinder')
        location = geolocator.geocode(f'{zip_code}, {city}, {state}', timeout=10)
        if location:
            return location.latitude, location.longitude
        else:
            print(f"Warning: Could not geocode ZIP {zip_code}. Using default location.")
            return 39.9612, -82.9988
    except Exception as e:
        print(f"Warning: Geocoding error: {e}. Using default location.")
        return 39.9612, -82.9988

def main():
    parser = argparse.ArgumentParser(description='Search gas prices using alternative methods')
    parser.add_argument('--city', type=str, default='Columbus', help='City name')
    parser.add_argument('--state', type=str, default='OH', help='State abbreviation')
    parser.add_argument('--zip', type=str, help='ZIP code (overrides lat/lon)')
    parser.add_argument('--lat', type=float, default=39.9612, help='Latitude (default: Columbus, OH)')
    parser.add_argument('--lon', type=float, default=-82.9988, help='Longitude (default: Columbus, OH)')
    parser.add_argument('--radius', type=float, default=20, help='Search radius in miles (default: 20)')
    parser.add_argument('--fuel', type=str, default='87', help='Fuel type (default: 87)')
    parser.add_argument('--base-price', type=float, default=2.89, help='Base price for estimation (default: 2.89)')
    parser.add_argument('--output', default='gas_prices.json', help='Output file')
    parser.add_argument('--summary', action='store_true', help='Print summary to stdout')

    args = parser.parse_args()

    # Geocode ZIP if provided
    if args.zip:
        print(f"Geocoding ZIP {args.zip}...")
        lat, lon = geocode_zip(args.zip, args.city, args.state)
        print(f"ZIP {args.zip} -> {lat}, {lon}")
    else:
        lat, lon = args.lat, args.lon

    print(f"Searching for gas stations near {lat}, {lon} (radius: {args.radius} miles)")

    # Search for all stations
    stations = search_gas_stations(lat, lon, args.radius, args.fuel)

    # Search for Costco specifically
    costco_stations = search_costco_locations(lat, lon, args.radius)

    # Add Costco to main list
    costco_with_prices = estimate_costco_prices(costco_stations, args.base_price)
    stations.extend(costco_with_prices)

    # Remove duplicates (by location - lat/lon within 0.0001 degrees)
    seen_locations = set()
    unique_stations = []
    for station in stations:
        lat = station.get('lat', 0)
        lon = station.get('lon', 0)
        # Round coordinates to avoid near-duplicates
        loc_key = (round(lat, 4), round(lon, 4))
        if loc_key not in seen_locations:
            seen_locations.add(loc_key)
            unique_stations.append(station)

    save_stations(unique_stations, args.output)

    # Generate and print summary
    if args.summary:
        summary = generate_summary(unique_stations, args.fuel)
        print("\n" + summary)

if __name__ == '__main__':
    main()


path: scripts/gasbuddy_search.py
content: #!/usr/bin/env python3
"""
Search for gas prices using GasBuddy.
Usage: python3 gasbuddy_search.py --fuel "87" --lat 39.9612 --lon -82.9988 --radius 20 --output results.json
"""

import argparse
import json
import sys
from datetime import datetime

try:
    import requests
except ImportError:
    print("Error: Install required packages: pip install requests")
    sys.exit(1)

# GasBuddy API endpoints
GASBUDDY_API_BASE = "https://www.gasbuddy.com/graphql"

def search_stations(lat, lon, radius, fuel_type, api_key=None):
    """
    Search for gas stations near coordinates using GasBuddy.

    Note: GasBuddy's API requires authentication for full access.
    This script uses a workaround by scraping the public web interface.

    Parameters:
    - lat: Latitude (e.g., 39.9612 for Columbus, OH)
    - lon: Longitude (e.g., -82.9988 for Columbus, OH)
    - radius: Search radius in miles
    - fuel_type: Fuel type (87, 89, 91, diesel)
    - [REDACTED_SECRET] GasBuddy API key if you have one

    Returns:
    - List of stations with prices
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
        'Accept': 'application/json',
        'Accept-Language': 'en-US,en;q=0.9',
        'Content-Type': 'application/json',
        'Origin': 'https://www.gasbuddy.com',
        'Referer': 'https://www.gasbuddy.com/',
    }

    # Try using the public web API first
    try:
        # Search by location
        search_url = f"https://www.gasbuddy.com/home?search={lat}%2C{lon}&fuel={fuel_type}"

        response = requests.get(search_url, headers=headers, timeout=10)
        print(f"GasBuddy search status: {response.status_code}")

        if response.status_code == 200:
            # Parse HTML response (GasBuddy renders via client-side JS)
            # For now, we'll return a placeholder
            print("Note: GasBuddy requires JavaScript rendering. Consider using:")
            print("  - GasBuddy API (commercial access)")
            print("  - Playwright/Puppeteer for JS rendering")
            print("  - Alternative data sources")

        return scrape_with_playwright(lat, lon, radius, fuel_type)

    except Exception as e:
        print(f"Error searching GasBuddy: {e}")
        return []

def scrape_with_playwright(lat, lon, radius, fuel_type):
    """
    Alternative: Use Playwright for JavaScript rendering.

    This requires: pip install playwright && playwright install
    """
    try:
        from playwright.sync_api import sync_playwright

        stations = []

        with sync_playwright() as p:
            browser = p.chromium.launch(headless=True)
            page = browser.new_page()

            # Navigate to GasBuddy search
            url = f"https://www.gasbuddy.com/home?search={lat}%2C{lon}&fuel={fuel_type}"
            page.goto(url, wait_until="networkidle")

            # Wait for results to load
            page.wait_for_selector('[data-testid="station-row"]', timeout=10000)

            # Extract station data
            rows = page.query_selector_all('[data-testid="station-row"]')

            for row in rows:
                try:
                    station = parse_station_row(row, fuel_type)
                    if station:
                        stations.append(station)
                except Exception as e:
                    print(f"Error parsing station: {e}")
                    continue

            browser.close()

        return stations

    except ImportError:
        print("Playwright not installed. Install with: pip install playwright && playwright install")
        return []
    except Exception as e:
        print(f"Error with Playwright scraping: {e}")
        return []

def parse_station_row(row, fuel_type):
    """Parse a station row from GasBuddy."""
    try:
        # Extract station name
        name_elem = row.query_selector('[data-testid="station-name"]')
        name = name_elem.text_content(strip=True) if name_elem else "Unknown"

        # Extract address
        address_elem = row.query_selector('[data-testid="station-address"]')
        address = address_elem.text_content(strip=True) if address_elem else ""

        # Extract price
        price_elem = row.query_selector(f'[data-testid="fuel-price-{fuel_type}"]')
        price_text = price_elem.text_content(strip=True) if price_elem else ""

        # Parse price (e.g., "$2.89" -> 2.89)
        price = 0
        if price_text:
            try:
                price = float(price_text.replace('$', '').replace(',', ''))
            except ValueError:
                pass

        return {
            'source': 'gasbuddy',
            'name': name,
            'address': address,
            'price': price,
            'price_text': price_text,
            'fuel_type': fuel_type,
            'scraped_at': datetime.utcnow().isoformat()
        }

    except Exception as e:
        print(f"Error parsing row: {e}")
        return None

def save_stations(stations, output_file):
    """Save stations to JSON file."""
    with open(output_file, 'w') as f:
        json.dump(stations, f, indent=2, ensure_ascii=False)
    print(f"Saved {len(stations)} stations to {output_file}")

def main():
    parser = argparse.ArgumentParser(description='Search gas prices via GasBuddy')
    parser.add_argument('--lat', type=float, default=39.9612, help='Latitude (default: Columbus, OH)')
    parser.add_argument('--lon', type=float, default=-82.9988, help='Longitude (default: Columbus, OH)')
    parser.add_argument('--radius', type=float, default=20, help='Search radius in miles (default: 20)')
    parser.add_argument('--fuel', type=str, default='87', help='Fuel type: 87, 89, 91, diesel (default: 87)')
    parser.add_argument('--api-key', help='GasBuddy API key (optional)')
    parser.add_argument('--output', default='gas_prices.json', help='Output file')

    args = parser.parse_args()

    print(f"Searching for {args.fuel} octane gas near {args.lat}, {args.lon} (radius: {args.radius} miles)")

    stations = search_stations(args.lat, args.lon, args.radius, args.fuel, args.api_key)
    save_stations(stations, args.output)

    # Print top 5 cheapest
    sorted_stations = sorted([s for s in stations if s['price'] > 0], key=lambda x: x['price'])
    print(f"\nTop 5 cheapest {args.fuel} octane:")
    for i, station in enumerate(sorted_stations[:5], 1):
        print(f"{i}. {station['name']}: {station['price_text']} - {station['address']}")

if __name__ == '__main__':
    main()


path: test_all.json
content: source: osm
name: Kroger Fuel Center
brand: Kroger
lat: 40.0026306
lon: -83.1548227
distance: 8.76
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.361544

source: osm
name: Meijer
brand: Meijer
lat: 40.1480028
lon: -82.9258725
distance: 13.46
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.361920

source: osm
name: Speedway
brand: Speedway
lat: 40.0280525
lon: -83.1605987
distance: 9.75
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.362195

source: osm
name: Circle K
brand: Circle K
lat: 39.9908819
lon: -83.0350405
distance: 2.81
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.362449

source: osm
name: GetGo
brand: GetGo
lat: 40.159832
lon: -83.092306
distance: 14.57
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.362733

source: osm
name: BP
brand: BP
lat: 40.1145261
lon: -83.0899955
distance: 11.63
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.362983

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 39.981979
lon: -82.8170662
distance: 9.75
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.363221

source: osm
name: Speedway
brand: Speedway
lat: 39.9521949
lon: -83.1226745
distance: 6.61
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.363469

source: osm
name: Dairy Mart
brand: Unknown
lat: 40.104723
lon: -83.0888306
distance: 10.99
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.363736

source: osm
name: Shell
brand: Shell
lat: 40.1342825
lon: -83.0715544
distance: 12.55
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.363979

source: osm
name: Sunoco
brand: Sunoco
lat: 40.0147275
lon: -83.0109515
distance: 3.75
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.364214

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 39.9686922
lon: -82.9511168
distance: 2.58
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.364455

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 40.0169128
lon: -83.0125511
distance: 3.91
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.364718

source: osm
name: Marathon
brand: Marathon
lat: 39.9727424
lon: -83.1492308
distance: 8.03
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.364956

source: osm
name: BP
brand: BP
lat: 39.9725903
lon: -83.1504432
distance: 8.09
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.365192

source: osm
name: Circle K
brand: Circle K
lat: 40.1354317
lon: -82.9922056
distance: 12.03
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.365433

source: osm
name: Unknown Station
brand: Unknown
lat: 40.0781644
lon: -82.7334487
distance: 16.23
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.365694

source: osm
name: Kroger Gas Station
brand: Unknown
lat: 40.1025455
lon: -83.162502
distance: 13.06
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.365926

source: osm
name: Shell
brand: Shell
lat: 40.1371916
lon: -83.0165841
distance: 12.18
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.366156

source: osm
name: Shell
brand: Shell
lat: 39.9529489
lon: -82.6829429
distance: 16.78
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.366395

source: osm
name: BP
brand: BP
lat: 39.9515826
lon: -82.6834641
distance: 16.76
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.366650

source: osm
name: Speedway
brand: Speedway
lat: 39.9520975
lon: -82.6821576
distance: 16.82
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.366880

source: osm
name: BP Fuel Station
brand: BP
lat: 39.9459395
lon: -82.6824172
distance: 16.83
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.367108

source: osm
name: Marathon
brand: Unknown
lat: 40.1101704
lon: -83.2769791
distance: 17.98
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.367334

source: osm
name: Shell
brand: Shell
lat: 40.0011853
lon: -83.0007913
distance: 2.76
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.367595

source: osm
name: Speedway
brand: Speedway
lat: 39.9856677
lon: -82.7928143
distance: 11.06
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.367829

source: osm
name: Circle K
brand: Circle K
lat: 40.0247078
lon: -83.161346
distance: 9.67
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368011

source: osm
name: Kroger Gas
brand: Unknown
lat: 40.0052704
lon: -82.6647336
distance: 17.99
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368165

source: osm
name: BP
brand: BP
lat: 40.0028663
lon: -82.6781088
distance: 17.26
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368321

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 40.0753528
lon: -83.1416327
distance: 10.93
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368479

source: osm
name: Unknown Station
brand: Unknown
lat: 39.9394199
lon: -83.0144972
distance: 1.72
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368656

source: osm
name: Kroger
brand: Kroger
lat: 39.9076163
lon: -82.7778619
distance: 12.3
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368814

source: osm
name: Unknown Station
brand: Unknown
lat: 39.9844595
lon: -83.0651983
distance: 3.87
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.368965

source: osm
name: Speedway
brand: Speedway
lat: 40.0504863
lon: -83.0494218
distance: 6.72
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.369117

source: osm
name: United Dairy Farmers (UDF)
brand: Unknown
lat: 40.0331638
lon: -83.0006345
distance: 4.97
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.369269

source: osm
name: Marathon
brand: Marathon
lat: 40.060974
lon: -82.9777541
distance: 6.97
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.369424

source: osm
name: Marathon
brand: Marathon
lat: 40.0315049
lon: -82.9958193
distance: 4.85
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.369596

source: osm
name: Marathon
brand: Marathon
lat: 40.098937
lon: -83.1204912
distance: 11.49
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.369748

source: osm
name: Mobil
brand: Mobil
lat: 39.9847682
lon: -82.9570042
distance: 2.75
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.369900

source: osm
name: Speedway
brand: Speedway
lat: 40.0593271
lon: -82.9377496
distance: 7.51
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.370055

source: osm
name: Speedway
brand: Speedway
lat: 40.0071971
lon: -83.048318
distance: 4.12
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.370207

source: osm
name: Unknown Station
brand: Unknown
lat: 39.8261865
lon: -83.1403227
distance: 11.97
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.370501

source: osm
name: Shell
brand: Shell
lat: 40.0549604
lon: -82.8642813
distance: 9.63
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.370673

source: osm
name: Kroger Gas
brand: Kroger
lat: 39.9072263
lon: -82.7345806
distance: 14.52
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.370824

source: osm
name: BP
brand: BP
lat: 39.9279094
lon: -82.8307252
distance: 9.22
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.370974

source: osm
name: GetGo
brand: GetGo
lat: 39.9847224
lon: -83.0271927
distance: 2.21
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.371125

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 40.1475872
lon: -82.9226295
distance: 13.48
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.371279

source: osm
name: Marathon
brand: Marathon
lat: 39.9279224
lon: -83.11641
distance: 6.65
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.371438

source: osm
name: Sunoco
brand: Sunoco
lat: 39.9575112
lon: -83.0414208
distance: 2.28
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.371608

source: osm
name: Mobil
brand: Mobil
lat: 40.0257389
lon: -82.9614075
distance: 4.87
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.371769

source: osm
name: Shell
brand: Shell
lat: 40.0151493
lon: -82.9931475
distance: 3.73
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.371918

source: osm
name: Marathon
brand: Marathon
lat: 40.0148849
lon: -82.9905744
distance: 3.73
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.372070

source: osm
name: Circle K
brand: Circle K
lat: 40.058478
lon: -82.9384804
distance: 7.44
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.372220

source: osm
name: UDF Fuel
brand: United Dairy Farmers
lat: 40.0576816
lon: -82.9235558
distance: 7.76
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.372371

source: osm
name: Speedway
brand: Speedway
lat: 40.0329314
lon: -83.1298558
distance: 8.54
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.372531

source: osm
name: Marathon
brand: Marathon
lat: 40.0321445
lon: -83.1383234
distance: 8.88
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.372692

source: osm
name: UDF Fuel
brand: United Dairy Farmers
lat: 40.0321508
lon: -83.1397524
distance: 8.94
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.372854

source: osm
name: Kroger
brand: Kroger
lat: 39.955932
lon: -83.0945944
distance: 5.1
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373006

source: osm
name: Sunoco
brand: Sunoco
lat: 39.9518951
lon: -83.1309087
distance: 7.04
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373158

source: osm
name: GetGo
brand: GetGo
lat: 39.95277
lon: -83.1277141
distance: 6.87
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373334

source: osm
name: Thorntons
brand: Thorntons
lat: 39.9523716
lon: -83.1338188
distance: 7.19
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373487

source: osm
name: GetGo
brand: GetGo
lat: 39.87778
lon: -83.0614175
distance: 6.65
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373658

source: osm
name: CNG Trans
brand: Unknown
lat: 39.9256033
lon: -82.8321065
distance: 9.19
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373809

source: osm
name: Speedway
brand: Speedway
lat: 39.9232941
lon: -82.8304951
distance: 9.31
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.373960

source: osm
name: Kroger
brand: Kroger
lat: 39.9565654
lon: -82.7721547
distance: 12.04
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.374110

source: osm
name: BP
brand: BP
lat: 39.9096129
lon: -82.7777409
distance: 12.27
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.374263

source: osm
name: GetGo
brand: GetGo
lat: 39.9077165
lon: -82.7763714
distance: 12.38
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.374446

source: osm
name: Speedway
brand: Speedway
lat: 40.2144914
lon: -83.0327042
distance: 17.57
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.374624

source: osm
name: Mobil
brand: Mobil
lat: 39.9446404
lon: -83.1118772
distance: 6.11
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.374834

source: osm
name: Circle K
brand: Circle K
lat: 39.9438947
lon: -83.1122543
distance: 6.14
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375008

source: osm
name: Marathon
brand: Marathon
lat: 40.1047243
lon: -83.0890248
distance: 11
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375161

source: osm
name: Speedway
brand: Speedway
lat: 39.9564408
lon: -82.9191194
distance: 4.24
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375313

source: osm
name: Sunoco
brand: Sunoco
lat: 40.0756548
lon: -82.967311
distance: 8.07
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375478

source: osm
name: Certified
brand: Certified
lat: 39.9110588
lon: -83.1184361
distance: 7.23
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375654

source: osm
name: Sunoco
brand: Sunoco
lat: 39.9705733
lon: -83.0454176
distance: 2.56
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375806

source: osm
name: Unknown Station
brand: Unknown
lat: 39.8894477
lon: -83.0693711
distance: 6.21
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.375956

source: osm
name: Speedway
brand: Speedway
lat: 39.7995258
lon: -82.7084898
distance: 19.04
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.376107

source: osm
name: Speedway
brand: Speedway
lat: 39.8516795
lon: -82.8044811
distance: 12.79
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.376265

source: osm
name: Speedway
brand: Speedway
lat: 40.1507088
lon: -82.9977856
distance: 13.08
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.376418

source: osm
name: Speedway
brand: Speedway
lat: 40.1376383
lon: -83.0176527
distance: 12.21
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.376596

source: osm
name: Speedway
brand: Speedway
lat: 40.1163843
lon: -82.9277137
distance: 11.35
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.376749

source: osm
name: BP
brand: BP
lat: 40.1184953
lon: -82.9958837
distance: 10.85
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.376900

source: osm
name: Speedway
brand: Speedway
lat: 40.1184319
lon: -82.9949031
distance: 10.85
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377051

source: osm
name: Speedway
brand: Speedway
lat: 40.0955222
lon: -82.9258447
distance: 10.04
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377201

source: osm
name: Speedway
brand: Speedway
lat: 40.0574263
lon: -82.8988214
distance: 8.5
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377351

source: osm
name: Marathon
brand: Marathon
lat: 40.0573165
lon: -82.898239
distance: 8.51
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377501

source: osm
name: Speedway
brand: Speedway
lat: 40.0557478
lon: -82.8630671
distance: 9.72
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377670

source: osm
name: Sunoco
brand: Sunoco
lat: 39.9556784
lon: -82.7944765
distance: 10.85
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377820

source: osm
name: Speedway
brand: Speedway
lat: 39.980347
lon: -82.8316577
distance: 8.97
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.377970

source: osm
name: Speedway
brand: Speedway
lat: 39.9550865
lon: -82.8291955
distance: 9.01
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378125

source: osm
name: Speedway
brand: Speedway
lat: 39.9483237
lon: -82.9376243
distance: 3.37
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378274

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 39.960552
lon: -82.8769222
distance: 6.47
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378425

source: osm
name: Speedway
brand: Speedway
lat: 40.0131137
lon: -82.8665191
distance: 7.88
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378569

source: osm
name: Speedway
brand: Speedway
lat: 40.0197117
lon: -82.8910497
distance: 7
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378699

source: osm
name: Speedway
brand: Speedway
lat: 40.050208
lon: -82.9533465
distance: 6.6
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378828

source: osm
name: Speedway
brand: Speedway
lat: 40.0858944
lon: -82.9606455
distance: 8.84
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.378956

source: osm
name: Speedway
brand: Speedway
lat: 40.0238212
lon: -82.9959851
distance: 4.32
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379085

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 39.8786206
lon: -83.0538627
distance: 6.4
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379215

source: osm
name: Speedway
brand: Speedway
lat: 39.9381681
lon: -82.9965532
distance: 1.59
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379348

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 39.8818488
lon: -83.000488
distance: 5.48
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379486

source: osm
name: Speedway
brand: Speedway
lat: 39.8826022
lon: -83.0001992
distance: 5.42
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379634

source: osm
name: Speedway
brand: Speedway
lat: 39.8779482
lon: -83.053756
distance: 6.44
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379763

source: osm
name: Speedway
brand: Speedway
lat: 39.8901902
lon: -83.0862036
distance: 6.75
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.379892

source: osm
name: Speedway
brand: Speedway
lat: 39.9001536
lon: -83.079385
distance: 6
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380021

source: osm
name: Turkey Hill
brand: Turkey Hill
lat: 39.9061181
lon: -83.0751495
distance: 5.56
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380150

source: osm
name: Speedway
brand: Speedway
lat: 39.9272249
lon: -83.0569539
distance: 3.88
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380279

source: osm
name: Speedway
brand: Speedway
lat: 39.9429534
lon: -83.0375678
distance: 2.41
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380408

source: osm
name: Speedway
brand: Speedway
lat: 39.9881866
lon: -83.0261166
distance: 2.36
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380546

source: osm
name: Speedway
brand: Speedway
lat: 40.0544309
lon: -83.0673672
distance: 7.39
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380715

source: osm
name: Marathon
brand: Marathon
lat: 40.0634794
lon: -83.0565513
distance: 7.69
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.380879

source: osm
name: Speedway
brand: Speedway
lat: 40.0887591
lon: -83.0912313
distance: 10.07
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381010

source: osm
name: Speedway
brand: Speedway
lat: 40.0272481
lon: -83.0907869
distance: 6.68
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381139

source: osm
name: Speedway
brand: Speedway
lat: 40.0039031
lon: -83.1395814
distance: 8.03
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381269

source: osm
name: Speedway
brand: Speedway
lat: 39.9858331
lon: -83.1501962
distance: 8.21
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381399

source: osm
name: Speedway
brand: Speedway
lat: 39.9702196
lon: -83.0976702
distance: 5.29
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381540

source: osm
name: Speedway
brand: Speedway
lat: 39.9535145
lon: -83.0967923
distance: 5.23
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381679

source: osm
name: Speedway
brand: Speedway
lat: 39.9313046
lon: -83.1600617
distance: 8.81
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381833

source: osm
name: Speedway
brand: Speedway
lat: 39.9519034
lon: -83.1288276
distance: 6.93
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.381962

source: osm
name: Mobile
brand: Unknown
lat: 39.9548829
lon: -83.0696396
distance: 3.79
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382091

source: osm
name: Sunoco
brand: Sunoco
lat: 40.0804327
lon: -82.928158
distance: 9.04
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382234

source: osm
name: Sheetz
brand: Sheetz
lat: 40.0781981
lon: -82.8513519
distance: 11.24
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382363

source: osm
name: Circle K
brand: Circle K
lat: 40.1486767
lon: -83.0920219
distance: 13.85
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382493

source: osm
name: Sheetz
brand: Sheetz
lat: 39.8787813
lon: -82.931775
distance: 6.71
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382642

source: osm
name: Sheetz
brand: Sheetz
lat: 40.1743783
lon: -83.0225371
distance: 14.76
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382773

source: osm
name: Sheetz
brand: Sheetz
lat: 39.8618441
lon: -82.8270541
distance: 11.41
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.382902

source: osm
name: Sheetz
brand: Sheetz
lat: 39.9608527
lon: -83.37008
distance: 19.71
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.383154

source: osm
name: BP
brand: BP
lat: 39.9535115
lon: -83.1108454
distance: 5.97
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.383283

source: osm
name: Valero
brand: Valero
lat: 39.7197498
lon: -82.9527525
distance: 16.84
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.383710

source: osm
name: Meijer
brand: Meijer
lat: 40.0596482
lon: -82.8626458
distance: 9.92
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.383859

source: osm
name: Circle K
brand: Circle K
lat: 39.7151421
lon: -82.9877327
distance: 16.99
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.383998

source: osm
name: Sunoco
brand: Sunoco
lat: 39.7166069
lon: -82.9863652
distance: 16.89
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.384129

source: osm
name: Speedway
brand: Speedway
lat: 39.7159088
lon: -82.9862729
distance: 16.94
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.384259

source: osm
name: Unknown Station
brand: Unknown
lat: 40.059815
lon: -82.9722062
distance: 6.95
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.384398

source: osm
name: Unknown Station
brand: Unknown
lat: 40.1574731
lon: -83.0140492
distance: 13.57
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:20:11.384581

name: Costco Gas
address: 5000 Morse Rd, Columbus, OH 43213
lat: 39.9667
lon: -82.85
distance: 7.9087769870111195
price: 2.69
price_text: $2.69 (est.)
is_costco: yes

name: Costco Gas
address: 1350 Hilliard Rome Rd, Columbus, OH 43228
lat: 39.94
lon: -83.15
distance: 8.160788584610774
price: 2.69
price_text: $2.69 (est.)
is_costco: yes

name: Costco Gas
address: 8570 S Old State Rd, Lewis Center, OH 43035
lat: 40.18
lon: -83
distance: 15.096144917348742
price: 2.69
price_text: $2.69 (est.)
is_costco: yes

path: test_gas.json
content: source: osm
name: Kroger Fuel Center
brand: Kroger
lat: 40.0026306
lon: -83.1548227
distance: 8.76
fuel_type: 87
price: 0
price_text: N/A
scraped_at: 2026-02-10T21:07:43.092886

name: Costco Gas
address: 5000 Morse Rd, Columbus, OH 43213
lat: 39.9667
lon: -82.85
distance: 7.9087769870111195
price: 2.69
price_text: $2.69 (est.)
is_costco: yes

name: Costco Gas
address: 1350 Hilliard Rome Rd, Columbus, OH 43228
lat: 39.94
lon: -83.15
distance: 8.160788584610774
price: 2.69
price_text: $2.69 (est.)
is_costco: yes

name: Costco Gas
address: 8570 S Old State Rd, Lewis Center, OH 43035
lat: 40.18
lon: -83
distance: 15.096144917348742
price: 2.69
price_text: $2.69 (est.)
is_costco: yes

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)