🌁 API Documentation

Real-time fog detection API for San Francisco. Updated hourly via GitHub Actions.

Base URL

https://isitfoggyinsanfrancisco.com/api

Available Regions

golden-gate — Western SF, Golden Gate Bridge, Marin Headlands
downtown — Financial District, Transamerica Pyramid, Northeast SF

Endpoints

GET /api/regions/golden-gate

Get current fog conditions for the Golden Gate / Western SF region.

Response

{
  "region": "golden-gate",
  "fogLevel": "clear" | "light" | "moderate" | "heavy",
  "visibilityScore": 0-100,
  "timestamp": "2026-02-16T02:22:51.191Z",
  "landmarks": [
    {
      "name": "gg-bridge-south-tower",
      "visible": true,
      "similarity": 0.85
    },
    ...
  ]
}

GET /api/regions/downtown

Get current fog conditions for the Downtown / Northeast SF region.

Response

{
  "region": "downtown",
  "fogLevel": "clear" | "light" | "moderate" | "heavy",
  "visibilityScore": 0-100,
  "timestamp": "2026-02-16T02:22:48.356Z",
  "landmarks": [
    {
      "name": "transamerica-pyramid",
      "visible": true,
      "similarity": 0.73
    },
    ...
  ]
}

GET /api/regions/index

Get current fog conditions for all regions combined.

Response

[
  {
    "region": "golden-gate",
    "fogLevel": "clear",
    "visibilityScore": 67,
    "timestamp": "2026-02-16T02:22:51.191Z",
    "landmarks": [...]
  },
  {
    "region": "downtown",
    "fogLevel": "light",
    "visibilityScore": 50,
    "timestamp": "2026-02-16T02:22:48.356Z",
    "landmarks": [...]
  }
]

GET /api/history/YYYY-MM-DD

Get all hourly readings for a specific date (e.g., /api/history/2026-02-16).

Response

{
  "hours": [
    {"timestamp": "2026-02-16T00:00:00.000Z", "regions": {...}},  // hour 0
    {"timestamp": "2026-02-16T01:00:00.000Z", "regions": {...}},  // hour 1
    {"timestamp": "2026-02-16T02:00:00.000Z", "regions": {...}},  // hour 2
    null,  // hour 3 - no data yet
    ...
    null   // hour 23 - no data yet
  ]
}

Note: Always contains exactly 24 items (indices 0-23 for hours 0-23 UTC). Future hours or missing data are null. Access by hour: data.hours[15] for 3pm UTC. File size ~2-5 KB.

GET /api/history

Get the available date range for historical data. Use this to know what dates you can query.

Response

{
  "startDate": "2024-02-16",
  "endDate": "2026-02-16",
  "totalDays": 730,
  "lastUpdated": "2026-02-16T11:00:00.000Z"
}

Note: Updated hourly with new data and daily when old data is cleaned up.

Response Fields

region string — Region identifier
fogLevel "clear" | "light" | "moderate" | "heavy" — Categorical fog assessment
visibilityScore number (0-100) — Percentage of landmarks visible
timestamp string (ISO 8601) — When this reading was taken
landmarks array — Per-landmark visibility details

Fog Level Thresholds

Update Frequency

Data is updated hourly via GitHub Actions cron schedule.

CORS

GitHub Pages serves all files with CORS headers enabled, so you can fetch from any origin.

Example Usage

// Fetch Golden Gate region fog status
fetch('https://isitfoggyinsanfrancisco.com/api/regions/golden-gate')
  .then(res => res.json())
  .then(data => console.log(data.fogLevel));

// Fetch all regions
fetch('https://isitfoggyinsanfrancisco.com/api/regions/index')
  .then(res => res.json())
  .then(regions => {
    regions.forEach(r => {
      console.log(`${r.region}: ${r.fogLevel} (${r.visibilityScore}%)`);
    });
  });

// Fetch multiple days (e.g., last 3 days)
const dates = ['2026-02-14', '2026-02-15', '2026-02-16'];
Promise.all(dates.map(d =>
  fetch(`https://isitfoggyinsanfrancisco.com/api/history/${d}`).then(r => r.json())
)).then(days => {
  // Flatten all hours, filter nulls
  const allHours = days.flatMap(day => day.hours.filter(h => h !== null));
  const avgVisibility = allHours.reduce((sum, h) =>
    sum + h.regions['golden-gate'].visibilityScore, 0) / allHours.length;
  console.log(`Avg visibility: ${avgVisibility.toFixed(1)}%`);
});

// Fetch specific day and access by hour
fetch('https://isitfoggyinsanfrancisco.com/api/history/2026-02-16')
  .then(res => res.json())
  .then(data => {
    // Access 3pm (hour 15) directly
    const reading3pm = data.hours[15];
    if (reading3pm) {
      console.log(`3pm visibility: ${reading3pm.regions['golden-gate'].visibilityScore}%`);
    } else {
      console.log('3pm data not available yet');
    }

    // Count available hours
    const available = data.hours.filter(h => h !== null).length;
    console.log(`${available} of 24 hours recorded`);
  });

// Check available date range
fetch('https://isitfoggyinsanfrancisco.com/api/history')
  .then(res => res.json())
  .then(range => {
    console.log(`Data available from ${range.startDate} to ${range.endDate}`);
    console.log(`Total days: ${range.totalDays}`);
  });

View on GitHub