# OurDigital SEO Audit - API Reference

## Google Search Console API

### Authentication
```python
from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly']
credentials = service_account.Credentials.from_service_account_file(
    'service-account-key.json', scopes=SCOPES
)
service = build('searchconsole', 'v1', credentials=credentials)
```

### Endpoints

#### Search Analytics
```python
# Get search performance data
request = {
    'startDate': '2024-01-01',
    'endDate': '2024-12-31',
    'dimensions': ['query', 'page', 'country', 'device'],
    'rowLimit': 25000,
    'dimensionFilterGroups': [{
        'filters': [{
            'dimension': 'country',
            'expression': 'kor'
        }]
    }]
}
response = service.searchanalytics().query(
    siteUrl='sc-domain:example.com',
    body=request
).execute()
```

#### URL Inspection
```python
request = {
    'inspectionUrl': 'https://example.com/page',
    'siteUrl': 'sc-domain:example.com'
}
response = service.urlInspection().index().inspect(body=request).execute()
```

#### Sitemaps
```python
# List sitemaps
sitemaps = service.sitemaps().list(siteUrl='sc-domain:example.com').execute()

# Submit sitemap
service.sitemaps().submit(
    siteUrl='sc-domain:example.com',
    feedpath='https://example.com/sitemap.xml'
).execute()
```

### Rate Limits
- 1,200 queries per minute per project
- 25,000 rows max per request

---

## PageSpeed Insights API

### Authentication
```python
import requests

API_KEY = 'your-api-key'
BASE_URL = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed'
```

### Request Parameters
```python
params = {
    'url': 'https://example.com',
    'key': API_KEY,
    'strategy': 'mobile',  # or 'desktop'
    'category': ['performance', 'accessibility', 'best-practices', 'seo']
}
response = requests.get(BASE_URL, params=params)
```

### Response Structure
```json
{
  "lighthouseResult": {
    "categories": {
      "performance": { "score": 0.85 },
      "seo": { "score": 0.92 }
    },
    "audits": {
      "largest-contentful-paint": {
        "numericValue": 2500,
        "displayValue": "2.5 s"
      },
      "cumulative-layout-shift": {
        "numericValue": 0.05
      },
      "total-blocking-time": {
        "numericValue": 150
      }
    }
  },
  "loadingExperience": {
    "metrics": {
      "LARGEST_CONTENTFUL_PAINT_MS": {
        "percentile": 2500,
        "category": "AVERAGE"
      }
    }
  }
}
```

### Core Web Vitals Thresholds

| Metric | Good | Needs Improvement | Poor |
|--------|------|-------------------|------|
| LCP | ≤ 2.5s | 2.5s - 4.0s | > 4.0s |
| FID | ≤ 100ms | 100ms - 300ms | > 300ms |
| CLS | ≤ 0.1 | 0.1 - 0.25 | > 0.25 |
| INP | ≤ 200ms | 200ms - 500ms | > 500ms |
| TTFB | ≤ 800ms | 800ms - 1800ms | > 1800ms |
| FCP | ≤ 1.8s | 1.8s - 3.0s | > 3.0s |

### Rate Limits
- 25,000 queries per day (free tier)
- No per-minute limit

---

## Google Analytics 4 Data API

### Authentication
```python
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import RunReportRequest

client = BetaAnalyticsDataClient()
property_id = '123456789'
```

### Common Reports

#### Traffic Overview
```python
request = RunReportRequest(
    property=f'properties/{property_id}',
    dimensions=[
        {'name': 'date'},
        {'name': 'sessionDefaultChannelGroup'}
    ],
    metrics=[
        {'name': 'sessions'},
        {'name': 'totalUsers'},
        {'name': 'screenPageViews'},
        {'name': 'bounceRate'}
    ],
    date_ranges=[{'start_date': '30daysAgo', 'end_date': 'today'}]
)
response = client.run_report(request)
```

#### Landing Pages
```python
request = RunReportRequest(
    property=f'properties/{property_id}',
    dimensions=[{'name': 'landingPage'}],
    metrics=[
        {'name': 'sessions'},
        {'name': 'engagementRate'},
        {'name': 'conversions'}
    ],
    date_ranges=[{'start_date': '30daysAgo', 'end_date': 'today'}],
    order_bys=[{'metric': {'metric_name': 'sessions'}, 'desc': True}],
    limit=100
)
```

### Useful Dimensions
- `date`, `dateHour`
- `sessionDefaultChannelGroup`
- `landingPage`, `pagePath`
- `deviceCategory`, `operatingSystem`
- `country`, `city`
- `sessionSource`, `sessionMedium`

### Useful Metrics
- `sessions`, `totalUsers`, `newUsers`
- `screenPageViews`, `engagementRate`
- `averageSessionDuration`
- `bounceRate`, `conversions`

---

## Google Trends API (pytrends)

### Installation
```bash
pip install pytrends
```

### Usage
```python
from pytrends.request import TrendReq

pytrends = TrendReq(hl='ko-KR', tz=540)

# Interest over time
pytrends.build_payload(['keyword1', 'keyword2'], timeframe='today 12-m', geo='KR')
interest_df = pytrends.interest_over_time()

# Related queries
related = pytrends.related_queries()

# Trending searches
trending = pytrends.trending_searches(pn='south_korea')

# Suggestions
suggestions = pytrends.suggestions('seo')
```

### Rate Limits
- No official limits, but implement delays (1-2 seconds between requests)
- May trigger CAPTCHA with heavy usage

---

## Custom Search JSON API

### Authentication
```python
import requests

API_KEY = 'your-api-key'
CX = 'your-search-engine-id'  # Programmable Search Engine ID
BASE_URL = 'https://www.googleapis.com/customsearch/v1'
```

### Request
```python
params = {
    'key': API_KEY,
    'cx': CX,
    'q': 'search query',
    'num': 10,  # 1-10
    'start': 1,  # Pagination
    'gl': 'kr',  # Country
    'hl': 'ko'   # Language
}
response = requests.get(BASE_URL, params=params)
```

### Response Structure
```json
{
  "searchInformation": {
    "totalResults": "12345",
    "searchTime": 0.5
  },
  "items": [
    {
      "title": "Page Title",
      "link": "https://example.com",
      "snippet": "Description...",
      "pagemap": {
        "metatags": [...],
        "cse_image": [...]
      }
    }
  ]
}
```

### Rate Limits
- 100 queries per day (free)
- 10,000 queries per day ($5 per 1,000)

---

## Knowledge Graph Search API

### Request
```python
API_KEY = 'your-api-key'
BASE_URL = 'https://kgsearch.googleapis.com/v1/entities:search'

params = {
    'key': API_KEY,
    'query': 'entity name',
    'types': 'Organization',
    'languages': 'ko',
    'limit': 10
}
response = requests.get(BASE_URL, params=params)
```

### Response
```json
{
  "itemListElement": [
    {
      "result": {
        "@type": "EntitySearchResult",
        "name": "Entity Name",
        "description": "Description...",
        "@id": "kg:/m/entity_id",
        "detailedDescription": {
          "articleBody": "..."
        }
      },
      "resultScore": 1234.56
    }
  ]
}
```

---

## Schema.org Reference

### JSON-LD Format
```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Company Name",
  "url": "https://example.com"
}
</script>
```

### Common Schema Types

#### Organization
```json
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Company Name",
  "url": "https://example.com",
  "logo": "https://example.com/logo.png",
  "sameAs": [
    "https://facebook.com/company",
    "https://twitter.com/company"
  ],
  "contactPoint": {
    "@type": "ContactPoint",
    "telephone": "+82-2-1234-5678",
    "contactType": "customer service"
  }
}
```

#### LocalBusiness
```json
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "name": "Business Name",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "123 Street",
    "addressLocality": "Seoul",
    "addressRegion": "Seoul",
    "postalCode": "12345",
    "addressCountry": "KR"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 37.5665,
    "longitude": 126.9780
  },
  "openingHoursSpecification": [{
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
    "opens": "09:00",
    "closes": "18:00"
  }]
}
```

#### Article/BlogPosting
```json
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "Article Title",
  "author": {
    "@type": "Person",
    "name": "Author Name"
  },
  "datePublished": "2024-01-01",
  "dateModified": "2024-01-15",
  "image": "https://example.com/image.jpg",
  "publisher": {
    "@type": "Organization",
    "name": "Publisher Name",
    "logo": {
      "@type": "ImageObject",
      "url": "https://example.com/logo.png"
    }
  }
}
```

#### Product
```json
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Product Name",
  "image": "https://example.com/product.jpg",
  "description": "Product description",
  "brand": {
    "@type": "Brand",
    "name": "Brand Name"
  },
  "offers": {
    "@type": "Offer",
    "price": "29900",
    "priceCurrency": "KRW",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.5",
    "reviewCount": "100"
  }
}
```

#### FAQPage
```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [{
    "@type": "Question",
    "name": "Question text?",
    "acceptedAnswer": {
      "@type": "Answer",
      "text": "Answer text."
    }
  }]
}
```

#### BreadcrumbList
```json
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [{
    "@type": "ListItem",
    "position": 1,
    "name": "Home",
    "item": "https://example.com/"
  }, {
    "@type": "ListItem",
    "position": 2,
    "name": "Category",
    "item": "https://example.com/category/"
  }]
}
```

#### WebSite (with SearchAction)
```json
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "Site Name",
  "url": "https://example.com",
  "potentialAction": {
    "@type": "SearchAction",
    "target": {
      "@type": "EntryPoint",
      "urlTemplate": "https://example.com/search?q={search_term_string}"
    },
    "query-input": "required name=search_term_string"
  }
}
```

---

## XML Sitemap Specification

### Format
```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page</loc>
    <lastmod>2024-01-15</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>
```

### Index Sitemap
```xml
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://example.com/sitemap-posts.xml</loc>
    <lastmod>2024-01-15</lastmod>
  </sitemap>
</sitemapindex>
```

### Limits
- 50,000 URLs max per sitemap
- 50MB uncompressed max
- Use index for larger sites

### Best Practices
- Use absolute URLs
- Include only canonical URLs
- Keep lastmod accurate
- Exclude noindex pages

---

## Robots.txt Reference

### Directives

```txt
# Comments start with #
User-agent: *
Disallow: /admin/
Disallow: /private/
Allow: /public/

User-agent: Googlebot
Disallow: /no-google/
Crawl-delay: 1

Sitemap: https://example.com/sitemap.xml
```

### Common User-agents
- `*` - All bots
- `Googlebot` - Google crawler
- `Googlebot-Image` - Google Image crawler
- `Bingbot` - Bing crawler
- `Yandex` - Yandex crawler
- `Baiduspider` - Baidu crawler

### Pattern Matching
- `*` - Wildcard (any sequence)
- `$` - End of URL
- `/path/` - Directory
- `/*.pdf$` - All PDFs

### Testing
```python
from urllib.robotparser import RobotFileParser

rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()

# Check if URL is allowed
can_fetch = rp.can_fetch("Googlebot", "https://example.com/page")
```

---

## Error Handling

### HTTP Status Codes

| Code | Meaning | Action |
|------|---------|--------|
| 200 | OK | Process response |
| 301/302 | Redirect | Follow or flag |
| 400 | Bad Request | Check parameters |
| 401 | Unauthorized | Check credentials |
| 403 | Forbidden | Check permissions |
| 404 | Not Found | Flag missing resource |
| 429 | Rate Limited | Implement backoff |
| 500 | Server Error | Retry with backoff |
| 503 | Service Unavailable | Retry later |

### Retry Strategy
```python
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10)
)
async def make_request(url):
    # Request logic
    pass
```
