TransitApp-MCP / API_INTEGRATION.md
JG1310's picture
Upload 12 files
1459a60 verified

A newer version of the Gradio SDK is available: 6.14.0

Upgrade

Integrating Real TransitApp API

This guide explains how to replace mock data with real TransitApp API calls.

Step 1: Get API Access

  1. Visit https://transitapp.com/apis
  2. Fill out the request form
  3. Wait for approval (usually 1-2 business days)
  4. You'll receive:
    • API key
    • Base URL (usually https://api.transitapp.com/v3)
    • Rate limits: 5 calls/minute, 1,500/month (free tier)

Step 2: Configure Environment

Add to your .env file:

TRANSIT_API_KEY=your_actual_api_key_here

Step 3: Update API Functions

Example: get_nearby_transit with Real API

Replace the mock implementation in app.py with:

def get_nearby_transit(
    latitude: float,
    longitude: float,
    radius: int = 500,
    transport_types: str = "all"
) -> str:
    """Get nearby transit using real TransitApp API"""
    
    if not TRANSIT_API_KEY:
        return json.dumps({
            "error": "TRANSIT_API_KEY not configured",
            "note": "Running in demo mode with mock data"
        })
    
    try:
        # TransitApp API endpoint for nearby transit
        url = f"{TRANSIT_API_BASE}/stops/nearby"
        
        headers = {
            "X-API-Key": TRANSIT_API_KEY,
            "Content-Type": "application/json"
        }
        
        params = {
            "lat": latitude,
            "lon": longitude,
            "radius": radius,
            "types": transport_types if transport_types != "all" else None
        }
        
        response = requests.get(url, headers=headers, params=params, timeout=10)
        response.raise_for_status()
        
        data = response.json()
        
        # Transform to our expected format
        result = {
            "location": {"lat": latitude, "lon": longitude},
            "radius_meters": radius,
            "timestamp": datetime.now().isoformat(),
            "nearby_lines": []
        }
        
        # Map TransitApp response to our format
        for stop in data.get("stops", []):
            for route in stop.get("routes", []):
                result["nearby_lines"].append({
                    "type": route.get("type"),
                    "route_number": route.get("short_name"),
                    "route_name": route.get("long_name"),
                    "direction": route.get("headsign"),
                    "next_arrivals": [
                        f"{arrival['minutes']} min" 
                        for arrival in route.get("arrivals", [])[:3]
                    ],
                    "stop_name": stop.get("name"),
                    "stop_distance_meters": stop.get("distance"),
                    "realtime": route.get("realtime", False),
                    "crowdsourced_location": route.get("go_enabled", False)
                })
        
        return json.dumps(result, indent=2)
        
    except requests.exceptions.RequestException as e:
        logger.error(f"API request failed: {e}")
        return json.dumps({
            "error": f"API request failed: {str(e)}",
            "fallback": "Using mock data"
        })
    except Exception as e:
        logger.error(f"Error in get_nearby_transit: {e}")
        return json.dumps({"error": str(e)})

Step 4: TransitApp API Endpoints Reference

Based on their API documentation, key endpoints include:

Nearby Stops/Stations

GET /v3/stops/nearby
Parameters:
  - lat: latitude
  - lon: longitude
  - radius: search radius in meters
  - types: comma-separated (bus,metro,train,bike)

Trip Planning

POST /v3/routes
Body:
  - origin: {lat, lon}
  - destination: {lat, lon}
  - time: departure time (ISO format or "now")
  - modes: array of allowed modes
  - preferences: {fastest|cheapest|fewest_transfers}

Real-time Vehicle Positions

GET /v3/vehicles/{route_id}
Parameters:
  - route_id: route identifier
  - direction: 0 (outbound) or 1 (inbound)

Service Alerts

GET /v3/alerts
Parameters:
  - lat: latitude
  - lon: longitude
  - radius: search radius
  - severity: alert severity filter

Step 5: Rate Limiting

Implement rate limiting to stay within API limits:

import time
from functools import wraps

class RateLimiter:
    def __init__(self, calls_per_minute=5):
        self.calls_per_minute = calls_per_minute
        self.calls = []
    
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            # Remove calls older than 1 minute
            self.calls = [c for c in self.calls if now - c < 60]
            
            if len(self.calls) >= self.calls_per_minute:
                # Wait until oldest call expires
                sleep_time = 60 - (now - self.calls[0])
                logger.warning(f"Rate limit reached, waiting {sleep_time:.1f}s")
                time.sleep(sleep_time)
                self.calls = []
            
            self.calls.append(now)
            return func(*args, **kwargs)
        
        return wrapper

# Apply to functions
rate_limiter = RateLimiter(calls_per_minute=5)

@rate_limiter
def get_nearby_transit(...):
    # Function implementation
    pass

Step 6: Caching

Add caching to reduce API calls:

from functools import lru_cache
import hashlib

def cache_key(*args, **kwargs):
    """Generate cache key from arguments"""
    key = str(args) + str(sorted(kwargs.items()))
    return hashlib.md5(key.encode()).hexdigest()

# Simple in-memory cache
cache = {}
CACHE_TTL = 30  # seconds

def cached_api_call(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        key = cache_key(*args, **kwargs)
        now = time.time()
        
        if key in cache:
            result, timestamp = cache[key]
            if now - timestamp < CACHE_TTL:
                logger.info(f"Cache hit for {func.__name__}")
                return result
        
        result = func(*args, **kwargs)
        cache[key] = (result, now)
        return result
    
    return wrapper

Step 7: Fallback Strategy

When API is unavailable, gracefully degrade:

def get_nearby_transit_with_fallback(lat, lon, radius, types):
    """Try real API, fall back to mock data"""
    try:
        if TRANSIT_API_KEY:
            return get_nearby_transit_real_api(lat, lon, radius, types)
    except Exception as e:
        logger.warning(f"API failed, using mock data: {e}")
    
    return get_nearby_transit_mock(lat, lon, radius, types)

Step 8: Testing with Real API

# Set your API key
export TRANSIT_API_KEY="your_key_here"

# Run tests
python test_server.py

# Start server
python app.py

Step 9: Monitoring

Add logging for API usage:

class APIMonitor:
    def __init__(self):
        self.calls_today = 0
        self.calls_minute = 0
        self.errors = 0
        self.reset_daily = None
        self.reset_minute = None
    
    def log_call(self, success=True):
        now = datetime.now()
        
        # Reset counters if needed
        if not self.reset_daily or now.date() > self.reset_daily:
            self.calls_today = 0
            self.reset_daily = now.date()
        
        if not self.reset_minute or now - self.reset_minute > timedelta(minutes=1):
            self.calls_minute = 0
            self.reset_minute = now
        
        self.calls_today += 1
        self.calls_minute += 1
        
        if not success:
            self.errors += 1
        
        logger.info(
            f"API Usage - Today: {self.calls_today}/1500, "
            f"Minute: {self.calls_minute}/5, "
            f"Errors: {self.errors}"
        )

monitor = APIMonitor()

Alternative: Open Transit APIs

If you can't get TransitApp API access, use these alternatives:

GTFS-RT Feeds (Free)

Many cities publish free GTFS Realtime feeds:

OpenTripPlanner (Open Source)

Self-host a routing engine:

HERE Public Transit API

Commercial but has free tier:

Resources

Support

If you run into issues:

  1. Check API key is correctly set
  2. Verify rate limits not exceeded
  3. Check API endpoint URLs
  4. Review response formats
  5. Enable debug logging

For TransitApp API support: