Troubleshooting
This guide helps you resolve common issues when using ServicePytan with the ServiceTitan API.
Authentication Issues
“Invalid Client Credentials” Error
Problem: Authentication fails with invalid credentials error.
Solutions:
Verify Credentials:
import servicepytan # Check your configuration conn = servicepytan.auth.servicepytan_connect(config_file="./config.json") print("Client ID:", conn.get('SERVICETITAN_CLIENT_ID', 'Not set')) print("Tenant ID:", conn.get('SERVICETITAN_TENANT_ID', 'Not set')) # Never print secrets in production!
Check Credential Sources:
Ensure
CLIENT_IDandCLIENT_SECRETmatch in ServiceTitanVerify
APP_KEYandTENANT_IDare correctCheck if API application is active in ServiceTitan
Validate Environment:
# Ensure you're using the correct environment conn = servicepytan.auth.servicepytan_connect( api_environment="production" # or "integration" )
“Application Not Found” Error
Problem: API returns application not found error.
Solutions:
Verify
APP_IDandAPP_KEYin Developer PortalEnsure API application is published and active
Check tenant association in ServiceTitan settings
Environment Variable Issues
Problem: Environment variables not loading correctly.
Solutions:
Check Variable Names:
# Verify environment variables are set echo $SERVICETITAN_CLIENT_ID echo $SERVICETITAN_TENANT_ID
Use .env File:
# Create .env file in project root cat > .env << EOF SERVICETITAN_CLIENT_ID=your_client_id SERVICETITAN_CLIENT_SECRET=your_client_secret SERVICETITAN_APP_KEY=your_app_key SERVICETITAN_TENANT_ID=your_tenant_id EOF
Manual Loading:
from dotenv import load_dotenv import os load_dotenv() print("Variables loaded:", bool(os.getenv('SERVICETITAN_CLIENT_ID')))
API Request Issues
“Rate Limit Exceeded” Error
Problem: API returns 429 rate limit errors.
Solutions:
Implement Retry Logic:
import time import servicepytan def safe_api_call(endpoint, options, max_retries=3): for attempt in range(max_retries): try: return endpoint.get_all(options) except requests.HTTPError as e: if e.response.status_code == 429: wait_time = 2 ** attempt # Exponential backoff print(f"Rate limited, waiting {wait_time} seconds...") time.sleep(wait_time) else: raise raise Exception("Max retries exceeded")
Reduce Request Frequency:
Increase page sizes (up to 500 for most endpoints)
Add delays between requests
Use caching for repeated data
“Request Timeout” Error
Problem: API requests timeout before completing.
Solutions:
Reduce Page Size:
# Instead of large pages options = {"pageSize": 50} # Smaller, more reliable
Add Request Timeouts:
import requests # Custom timeout handling try: data = endpoint.get_all(options) except requests.Timeout: print("Request timed out, trying smaller batch...")
Break Into Smaller Date Ranges:
# Instead of large date ranges jobs = data_service.get_jobs_completed_between( "2024-01-01", "2024-01-07" # Weekly chunks )
“Permission Denied” Error
Problem: API returns 403 permission denied errors.
Solutions:
Check API Application Permissions:
Verify endpoint access in ServiceTitan Developer Portal
Ensure required scopes are granted
Validate Tenant Access:
# Test basic access endpoint = servicepytan.Endpoint("settings", "business-units", conn=conn) business_units = endpoint.get_all({"pageSize": 1}) print("Access confirmed:", len(business_units) >= 0)
Review Integration Settings:
Check if integration is active
Verify correct tenant association
Data Issues
Empty Results When Data Expected
Problem: API returns empty results when data should exist.
Solutions:
Check Date Formats:
# Ensure correct timezone and format from datetime import datetime import pytz # Convert to UTC for API local_date = datetime(2024, 1, 1) utc_date = pytz.timezone('America/New_York').localize(local_date).astimezone(pytz.UTC) formatted_date = utc_date.strftime("%Y-%m-%dT%H:%M:%SZ")
Verify Filter Parameters:
# Debug filters step by step options = {"pageSize": 10} # Start simple all_jobs = endpoint.get_all(options) print(f"Total jobs without filters: {len(all_jobs)}") # Add filters gradually options["jobStatus"] = "Completed" filtered_jobs = endpoint.get_all(options) print(f"Completed jobs: {len(filtered_jobs)}")
Check Environment Data:
# Ensure you're checking the right environment print(f"Using environment: {conn.get('SERVICETITAN_API_ENVIRONMENT')}") print(f"API Root: {conn.get('api_root')}")
Incorrect Date/Time Data
Problem: Date and time values appear incorrect.
Solutions:
Configure Timezone:
conn = servicepytan.auth.servicepytan_connect( timezone="America/New_York" # Set your local timezone )
Parse Dates Correctly:
from datetime import datetime import pytz # Parse ServiceTitan date strings def parse_st_date(date_string, local_tz="America/New_York"): utc_date = datetime.fromisoformat(date_string.replace('Z', '+00:00')) local_tz = pytz.timezone(local_tz) return utc_date.astimezone(local_tz)
Performance Issues
Slow API Responses
Problem: API requests take too long to complete.
Solutions:
Optimize Page Sizes:
# Find optimal page size for your use case page_sizes = [50, 100, 200, 500] for size in page_sizes: start_time = time.time() options = {"pageSize": size} result = endpoint.get_page(options, page=1) duration = time.time() - start_time print(f"Page size {size}: {duration:.2f}s for {len(result['data'])} records")
Use Specific Filters:
# More specific filters = faster responses options = { "pageSize": 200, "active": "true", "jobStatus": "Completed", "completedOnOrAfter": "2024-01-01T00:00:00Z" }
Parallel Processing:
from concurrent.futures import ThreadPoolExecutor def get_jobs_by_status(status): endpoint = servicepytan.Endpoint("jpm", "jobs", conn=conn) return endpoint.get_all({"jobStatus": status}) statuses = ["Completed", "Scheduled", "InProgress"] with ThreadPoolExecutor(max_workers=3) as executor: results = list(executor.map(get_jobs_by_status, statuses))
Memory Usage Issues
Problem: Application uses too much memory with large datasets.
Solutions:
Process in Chunks:
def process_jobs_in_chunks(start_date, end_date, chunk_size=1000): page = 1 while True: options = { "pageSize": chunk_size, "page": page, "completedOnOrAfter": start_date, "completedBefore": end_date } chunk = endpoint.get_page(options, page=page) if not chunk['data']: break # Process chunk immediately yield chunk['data'] page += 1 for job_chunk in process_jobs_in_chunks("2024-01-01", "2024-12-31"): # Process each chunk for job in job_chunk: # Do something with job pass
Use Generators:
def stream_jobs(options): page = 1 while True: page_options = {**options, "page": page} result = endpoint.get_page(page_options, page=page) for job in result['data']: yield job if len(result['data']) < options.get('pageSize', 50): break page += 1 # Memory-efficient processing for job in stream_jobs({"jobStatus": "Completed"}): print(job['id'])
Reports Issues
“Report Not Found” Error
Problem: Cannot access custom reports.
Solutions:
Verify Report ID:
from servicepytan.reports import get_report_list # List available reports reports = get_report_list("operations", conn=conn) for report in reports['data']: print(f"{report['name']}: {report['id']}")
Check Report Category:
from servicepytan.reports import get_report_categories categories = get_report_categories(conn=conn) for category in categories['data']: print(f"Category: {category['name']}")
Report Parameters Validation
Problem: Report parameters are rejected.
Solutions:
Check Parameter Types:
report = servicepytan.Report("operations", "report_id", conn=conn) # Show required parameters report.show_param_types() # Get parameter metadata print(report.metadata.get('parameters', []))
Validate Dynamic Sets:
from servicepytan.reports import get_dynamic_set_list # Get valid values for dynamic parameters business_units = get_dynamic_set_list("business-units", conn=conn) print("Valid business unit IDs:", [unit[0] for unit in business_units])
Development Environment Setup
Module Import Issues
Problem: Cannot import servicepytan modules.
Solutions:
Verify Installation:
pip list | grep servicepytan pip install --upgrade servicepytan
Check Python Path:
import sys print("Python path:", sys.path) try: import servicepytan print("ServicePytan version:", servicepytan.__version__) except ImportError as e: print("Import error:", e)
Virtual Environment:
# Create clean environment python -m venv venv_servicepytan source venv_servicepytan/bin/activate # Linux/Mac # or venv_servicepytan\Scripts\activate # Windows pip install servicepytan
Dependency Conflicts
Problem: Package dependency conflicts.
Solutions:
Check Dependencies:
pip check pip list --outdated
Install Specific Versions:
# Create requirements.txt with specific versions pip freeze > requirements.txt # Or install specific version pip install servicepytan==0.3.2
Debugging Tips
Enable Verbose Logging
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('servicepytan')
logger.setLevel(logging.DEBUG)
# Now all API requests will be logged
conn = servicepytan.auth.servicepytan_connect()
Test Connection
def test_servicepytan_connection():
"""Comprehensive connection test"""
try:
# Test authentication
conn = servicepytan.auth.servicepytan_connect()
print("✅ Configuration loaded")
# Test token retrieval
token = servicepytan.auth.get_auth_token(conn)
print("✅ Authentication successful")
# Test API access
endpoint = servicepytan.Endpoint("settings", "business-units", conn=conn)
data = endpoint.get_all({"pageSize": 1})
print("✅ API access confirmed")
# Test DataService
data_service = servicepytan.DataService(conn=conn)
print("✅ DataService initialized")
print("🎉 All tests passed!")
except Exception as e:
print(f"❌ Test failed: {e}")
import traceback
traceback.print_exc()
test_servicepytan_connection()
API Response Inspection
def debug_api_response(endpoint, options):
"""Debug API responses step by step"""
import json
print(f"Testing endpoint: {endpoint.folder}/{endpoint.endpoint}")
print(f"Options: {json.dumps(options, indent=2)}")
try:
# Test single page first
response = endpoint.get_page(options, page=1)
print(f"✅ Page 1: {len(response.get('data', []))} records")
print(f"Total pages: {response.get('totalPages', 'unknown')}")
print(f"Total count: {response.get('totalCount', 'unknown')}")
if response.get('data'):
print("Sample record keys:", list(response['data'][0].keys()))
except Exception as e:
print(f"❌ Error: {e}")
import traceback
traceback.print_exc()
# Usage
conn = servicepytan.auth.servicepytan_connect()
endpoint = servicepytan.Endpoint("jpm", "jobs", conn=conn)
debug_api_response(endpoint, {"pageSize": 5, "active": "true"})
Getting Help
If you can’t resolve your issue:
Check the GitHub Issues: https://github.com/elliotpalmer/servicepytan/issues
ServiceTitan Developer Docs: https://developer.servicetitan.io/
Create a New Issue: Include:
ServicePytan version
Python version
Complete error message
Minimal code example
Expected vs actual behavior