API Design

📖 9 min read 📄 Part 5 of 10

CDN Network - API Design

Content Delivery API

Standard Content Request

GET /images/hero-banner.webp HTTP/2
Host: assets.example.com
Accept: image/webp,image/png,*/*
Accept-Encoding: gzip, br
If-None-Match: "abc123def456"
If-Modified-Since: Wed, 15 Jan 2025 10:00:00 GMT

--- Response (Cache Hit) ---
HTTP/2 200 OK
Content-Type: image/webp
Content-Length: 245760
Cache-Control: public, max-age=86400, stale-while-revalidate=3600
ETag: "abc123def456"
Last-Modified: Wed, 15 Jan 2025 10:00:00 GMT
Age: 7200
X-Cache: HIT
X-Cache-Edge: us-east-1-iad
X-Request-Id: req-7f3a2b1c
Vary: Accept-Encoding
CF-Ray: 8a1b2c3d4e5f

[binary content]

Conditional Requests (304 Not Modified)

GET /styles/main.css HTTP/2
Host: assets.example.com
If-None-Match: "v2.5.1-hash"

--- Response ---
HTTP/2 304 Not Modified
ETag: "v2.5.1-hash"
Cache-Control: public, max-age=604800
Age: 43200
X-Cache: HIT

Range Requests (Partial Content)

GET /videos/episode-1.mp4 HTTP/2
Host: stream.example.com
Range: bytes=1048576-2097151

--- Response ---
HTTP/2 206 Partial Content
Content-Type: video/mp4
Content-Range: bytes 1048576-2097151/104857600
Content-Length: 1048576
Accept-Ranges: bytes
X-Cache: HIT

Cache Header Semantics

Cache-Control directives honored by CDN:
- public:                  Cache at edge (default for GET 200)
- private:                 Do NOT cache at edge (pass through)
- no-cache:                Cache but revalidate on every request
- no-store:                Never cache, never store
- max-age=N:               Cache for N seconds
- s-maxage=N:              CDN-specific max-age (overrides max-age)
- stale-while-revalidate=N: Serve stale for N seconds while refreshing
- stale-if-error=N:        Serve stale for N seconds if origin errors
- immutable:               Never revalidate (versioned assets)
- no-transform:            Do not modify content (no compression/optimization)

CDN-specific headers:
- CDN-Cache-Control:       CDN-only directives (not forwarded to client)
- Surrogate-Control:       Akamai-style CDN control header
- X-Cache-TTL:             Override TTL for this response

Cache Management API

Purge by URL

POST /api/v1/distributions/{dist_id}/purge
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "type": "url",
    "items": [
        "https://assets.example.com/images/hero-banner.webp",
        "https://assets.example.com/styles/main.css"
    ],
    "options": {
        "include_variants": true,
        "soft_purge": false
    }
}

--- Response ---
HTTP/1.1 202 Accepted
{
    "purge_id": "purge-a1b2c3d4",
    "status": "in_progress",
    "items_count": 2,
    "estimated_completion_seconds": 5,
    "edges_targeted": 247,
    "created_at": "2025-01-15T10:30:00Z"
}

Purge by Prefix/Wildcard

POST /api/v1/distributions/{dist_id}/purge
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "type": "prefix",
    "items": [
        "https://assets.example.com/images/products/*",
        "https://assets.example.com/api/v2/catalog/*"
    ]
}

--- Response ---
HTTP/1.1 202 Accepted
{
    "purge_id": "purge-e5f6g7h8",
    "status": "in_progress",
    "estimated_objects_affected": 15000,
    "estimated_completion_seconds": 30
}

Purge All (Full Invalidation)

POST /api/v1/distributions/{dist_id}/purge
Authorization: Bearer <api_token>

{
    "type": "all",
    "confirm": true
}

--- Response ---
HTTP/1.1 202 Accepted
{
    "purge_id": "purge-full-i9j0k1",
    "status": "in_progress",
    "warning": "Full purge will temporarily increase origin load",
    "estimated_completion_seconds": 60
}

Cache Prefetch/Warm

POST /api/v1/distributions/{dist_id}/prefetch
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "urls": [
        "https://assets.example.com/videos/new-release.mp4",
        "https://assets.example.com/images/campaign-2025.webp"
    ],
    "regions": ["us-east", "eu-west", "ap-northeast"],
    "priority": "high",
    "headers": {
        "Accept-Encoding": "br"
    }
}

--- Response ---
HTTP/1.1 202 Accepted
{
    "prefetch_id": "pf-m2n3o4p5",
    "urls_count": 2,
    "regions_targeted": 3,
    "edges_targeted": 45,
    "status": "in_progress"
}

Get Purge Status

GET /api/v1/distributions/{dist_id}/purge/purge-a1b2c3d4
Authorization: Bearer <api_token>

--- Response ---
HTTP/1.1 200 OK
{
    "purge_id": "purge-a1b2c3d4",
    "status": "completed",
    "items_count": 2,
    "edges_completed": 247,
    "edges_total": 247,
    "created_at": "2025-01-15T10:30:00Z",
    "completed_at": "2025-01-15T10:30:04Z",
    "duration_ms": 4200
}

Configuration API

Create Distribution

POST /api/v1/distributions
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "name": "Production Assets",
    "origins": [
        {
            "domain_name": "origin.example.com",
            "protocol": "https",
            "port": 443,
            "path": "/assets",
            "custom_headers": {
                "X-Origin-Auth": "secret-token"
            },
            "connection_timeout_ms": 5000,
            "read_timeout_ms": 30000
        }
    ],
    "default_cache_behavior": {
        "viewer_protocol_policy": "redirect-to-https",
        "allowed_methods": ["GET", "HEAD", "OPTIONS"],
        "cache_policy": "optimized-static",
        "compress": true,
        "default_ttl": 86400,
        "max_ttl": 31536000
    },
    "alternate_domain_names": ["assets.example.com", "cdn.example.com"],
    "certificate_id": "cert-x1y2z3",
    "price_class": "all_edges",
    "http_version": "http2and3",
    "ipv6_enabled": true,
    "logging": {
        "enabled": true,
        "bucket": "s3://cdn-logs-bucket/production/",
        "include_cookies": false
    }
}

--- Response ---
HTTP/1.1 201 Created
{
    "distribution_id": "dist-q5r6s7t8",
    "domain_name": "d1234abcdef.cdn.example.net",
    "status": "deploying",
    "estimated_deploy_minutes": 5,
    "created_at": "2025-01-15T10:00:00Z"
}

Update Origin Configuration

PUT /api/v1/distributions/{dist_id}/origins/{origin_id}
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "domain_name": "origin-v2.example.com",
    "failover": {
        "origin_id": "origin-backup-456",
        "status_codes": [500, 502, 503, 504],
        "timeout_ms": 10000
    },
    "health_check": {
        "path": "/health",
        "interval_seconds": 30,
        "healthy_threshold": 3,
        "unhealthy_threshold": 2
    }
}

--- Response ---
HTTP/1.1 200 OK
{
    "origin_id": "origin-123",
    "status": "deploying",
    "config_version": 5
}

Set Cache Behaviors

POST /api/v1/distributions/{dist_id}/cache-behaviors
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "path_pattern": "/api/v2/*",
    "priority": 1,
    "origin_id": "origin-api-789",
    "cache_policy": {
        "min_ttl": 0,
        "max_ttl": 60,
        "default_ttl": 10,
        "query_string": {
            "behavior": "whitelist",
            "keys": ["page", "limit", "sort"]
        },
        "headers": {
            "behavior": "whitelist",
            "keys": ["Authorization", "Accept-Language"]
        },
        "cookies": {
            "behavior": "none"
        }
    },
    "viewer_protocol_policy": "https-only",
    "allowed_methods": ["GET", "HEAD", "OPTIONS", "POST"],
    "compress": true
}

--- Response ---
HTTP/1.1 201 Created
{
    "behavior_id": "cb-u8v9w0x1",
    "path_pattern": "/api/v2/*",
    "priority": 1,
    "status": "deploying"
}

Analytics API

Get Bandwidth Statistics

GET /api/v1/distributions/{dist_id}/analytics/bandwidth
    ?start=2025-01-14T00:00:00Z
    &end=2025-01-15T00:00:00Z
    &granularity=1hour
    &regions=us-east,eu-west
Authorization: Bearer <api_token>

--- Response ---
HTTP/1.1 200 OK
{
    "distribution_id": "dist-q5r6s7t8",
    "period": {
        "start": "2025-01-14T00:00:00Z",
        "end": "2025-01-15T00:00:00Z"
    },
    "granularity": "1hour",
    "totals": {
        "bytes_sent": 8547321856000,
        "bytes_received": 125478963200,
        "requests": 1247856321
    },
    "time_series": [
        {
            "timestamp": "2025-01-14T00:00:00Z",
            "bytes_sent": 312456789000,
            "bytes_received": 4567890000,
            "requests": 45678901
        }
    ]
}

Get Cache Hit Ratio

GET /api/v1/distributions/{dist_id}/analytics/cache
    ?start=2025-01-14T00:00:00Z
    &end=2025-01-15T00:00:00Z
    &granularity=5min
Authorization: Bearer <api_token>

--- Response ---
HTTP/1.1 200 OK
{
    "totals": {
        "hit_ratio": 0.934,
        "hits": 1165456789,
        "misses": 82399532,
        "expired": 12456789,
        "bypass": 5678901
    },
    "time_series": [
        {
            "timestamp": "2025-01-14T00:00:00Z",
            "hit_ratio": 0.941,
            "hits": 4056789,
            "misses": 253211
        }
    ],
    "by_content_type": {
        "image/*": {"hit_ratio": 0.97, "requests": 500000000},
        "text/css": {"hit_ratio": 0.95, "requests": 200000000},
        "application/javascript": {"hit_ratio": 0.93, "requests": 300000000},
        "text/html": {"hit_ratio": 0.72, "requests": 150000000},
        "application/json": {"hit_ratio": 0.55, "requests": 97856321}
    }
}

Get Latency Percentiles

GET /api/v1/distributions/{dist_id}/analytics/latency
    ?start=2025-01-14T00:00:00Z
    &end=2025-01-15T00:00:00Z
    &granularity=1hour
    &percentiles=p50,p90,p95,p99
Authorization: Bearer <api_token>

--- Response ---
HTTP/1.1 200 OK
{
    "totals": {
        "p50_ms": 12.4,
        "p90_ms": 45.2,
        "p95_ms": 78.6,
        "p99_ms": 156.3,
        "avg_ms": 23.7
    },
    "by_cache_status": {
        "hit": {"p50_ms": 8.2, "p95_ms": 32.1, "p99_ms": 65.4},
        "miss": {"p50_ms": 145.6, "p95_ms": 320.8, "p99_ms": 890.2}
    },
    "by_region": {
        "us-east": {"p50_ms": 8.1, "p95_ms": 28.4},
        "eu-west": {"p50_ms": 11.3, "p95_ms": 42.7},
        "ap-northeast": {"p50_ms": 15.8, "p95_ms": 55.2}
    },
    "time_series": [
        {
            "timestamp": "2025-01-14T00:00:00Z",
            "p50_ms": 11.8,
            "p95_ms": 72.3,
            "p99_ms": 148.9
        }
    ]
}

Get Error Rate Analytics

GET /api/v1/distributions/{dist_id}/analytics/errors
    ?start=2025-01-14T00:00:00Z
    &end=2025-01-15T00:00:00Z
Authorization: Bearer <api_token>

--- Response ---
HTTP/1.1 200 OK
{
    "error_rate": 0.0023,
    "total_errors": 2867543,
    "by_status_code": {
        "400": 456789,
        "403": 234567,
        "404": 1567890,
        "500": 123456,
        "502": 234567,
        "503": 178901,
        "504": 71373
    },
    "by_type": {
        "origin_error": 608297,
        "client_error": 2259246,
        "edge_error": 0
    },
    "top_error_urls": [
        {"url": "/api/v2/users/profile", "count": 45678, "status": 500},
        {"url": "/images/deleted-asset.png", "count": 34567, "status": 404}
    ]
}

SSL/TLS Certificate API

Upload Custom Certificate

POST /api/v1/certificates
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "certificate": "-----BEGIN CERTIFICATE-----\nMIIF...\n-----END CERTIFICATE-----",
    "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIE...\n-----END RSA PRIVATE KEY-----",
    "certificate_chain": "-----BEGIN CERTIFICATE-----\nMIIG...\n-----END CERTIFICATE-----",
    "domain_names": ["assets.example.com", "*.example.com"]
}

--- Response ---
HTTP/1.1 201 Created
{
    "certificate_id": "cert-a1b2c3d4",
    "domain_names": ["assets.example.com", "*.example.com"],
    "issuer": "DigiCert Inc",
    "not_before": "2025-01-01T00:00:00Z",
    "not_after": "2026-01-01T00:00:00Z",
    "key_algorithm": "RSA-2048",
    "status": "pending_deployment",
    "fingerprint_sha256": "AB:CD:EF:12:34..."
}

Request Managed Certificate (Auto-Provisioned)

POST /api/v1/certificates/managed
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "domain_names": ["new-site.example.com"],
    "validation_method": "dns",
    "key_algorithm": "ecdsa-p256",
    "auto_renew": true
}

--- Response ---
HTTP/1.1 201 Created
{
    "certificate_id": "cert-managed-e5f6",
    "status": "pending_validation",
    "validation_records": [
        {
            "domain": "new-site.example.com",
            "type": "CNAME",
            "name": "_acme-challenge.new-site.example.com",
            "value": "validation-token.cdn.example.net"
        }
    ]
}

Deploy Certificate to Distribution

PUT /api/v1/distributions/{dist_id}/certificate
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "certificate_id": "cert-a1b2c3d4",
    "minimum_protocol_version": "TLSv1.2",
    "ssl_support_method": "sni-only"
}

--- Response ---
HTTP/1.1 200 OK
{
    "status": "deploying",
    "edges_deployed": 0,
    "edges_total": 247,
    "estimated_completion_minutes": 10
}

Edge Function/Worker API

Deploy Edge Function

POST /api/v1/distributions/{dist_id}/edge-functions
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "name": "image-optimizer",
    "runtime": "javascript",
    "code": "export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname.startsWith('/images/')) { const width = url.searchParams.get('w') || 800; return fetch(request, { cf: { image: { width: parseInt(width), quality: 80, format: 'webp' } } }); } return fetch(request); } }",
    "event_type": "viewer-request",
    "memory_limit_mb": 128,
    "timeout_ms": 5000,
    "environment_variables": {
        "OPTIMIZATION_QUALITY": "80",
        "MAX_WIDTH": "2048"
    }
}

--- Response ---
HTTP/1.1 201 Created
{
    "function_id": "fn-g7h8i9j0",
    "name": "image-optimizer",
    "version": 1,
    "status": "deploying",
    "code_size_bytes": 456,
    "deployed_edges": 0,
    "created_at": "2025-01-15T10:00:00Z"
}

Associate Function with Cache Behavior

PUT /api/v1/distributions/{dist_id}/cache-behaviors/{behavior_id}/functions
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "associations": [
        {
            "event_type": "viewer-request",
            "function_id": "fn-g7h8i9j0"
        },
        {
            "event_type": "origin-response",
            "function_id": "fn-k1l2m3n4"
        }
    ]
}

--- Response ---
HTTP/1.1 200 OK
{
    "behavior_id": "cb-u8v9w0x1",
    "function_associations": [
        {"event_type": "viewer-request", "function_id": "fn-g7h8i9j0"},
        {"event_type": "origin-response", "function_id": "fn-k1l2m3n4"}
    ],
    "status": "deploying"
}

Edge Function Event Types

viewer-request:   Runs before cache lookup; can modify request or return response
viewer-response:  Runs after cache/origin response; can modify response headers
origin-request:   Runs before origin fetch (only on cache miss); can modify origin request
origin-response:  Runs after origin response; can modify before caching

Real-Time Log Streaming API

Create Log Subscription

POST /api/v1/distributions/{dist_id}/log-subscriptions
Authorization: Bearer <api_token>
Content-Type: application/json

{
    "name": "production-logs",
    "destination": {
        "type": "kinesis",
        "stream_arn": "arn:aws:kinesis:us-east-1:123456789:stream/cdn-logs",
        "role_arn": "arn:aws:iam::123456789:role/cdn-log-writer"
    },
    "fields": [
        "timestamp", "client_ip", "method", "host", "path",
        "status_code", "response_bytes", "cache_status",
        "time_to_first_byte", "total_time_ms", "user_agent",
        "tls_version", "country"
    ],
    "sampling_rate": 100,
    "filter": {
        "status_codes": [500, 502, 503, 504],
        "cache_status": ["MISS", "ERROR"]
    }
}

--- Response ---
HTTP/1.1 201 Created
{
    "subscription_id": "logs-o4p5q6r7",
    "status": "active",
    "fields_count": 13,
    "estimated_volume_gb_per_day": 45.2
}

Supported Destinations

- Amazon Kinesis Data Streams
- Amazon S3 (batched, 5-minute intervals)
- Google Cloud Pub/Sub
- Azure Event Hubs
- Datadog
- Splunk HEC
- Custom HTTPS endpoint (webhook)
- Apache Kafka (self-hosted)

Rate Limiting and Quotas

API Rate Limits

Endpoint Category Rate Limit Burst
Configuration (CRUD) 100 req/min 20
Purge requests 1000 req/min 100
Prefetch requests 500 req/min 50
Analytics queries 300 req/min 30
Certificate operations 50 req/min 10
Edge function deploys 100 req/min 20
Log subscription mgmt 50 req/min 10

Rate Limit Response

HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312812

{
    "error": "rate_limit_exceeded",
    "message": "API rate limit exceeded. Retry after 12 seconds.",
    "limit": 100,
    "window": "1 minute",
    "retry_after_seconds": 12
}

API Authentication

Authentication Methods

1. API Token (Bearer):
   Authorization: Bearer cdn_live_a1b2c3d4e5f6...
   - Long-lived tokens for server-to-server
   - Scoped permissions (read-only, purge-only, full-access)

2. Signed Requests (HMAC):
   X-CDN-Timestamp: 1705312800
   X-CDN-Signature: sha256=abc123...
   - Time-limited, replay-protected
   - Used for high-security operations

3. OAuth 2.0:
   Authorization: Bearer eyJhbGciOiJSUzI1NiI...
   - For user-facing dashboards
   - Short-lived access tokens + refresh tokens

4. IAM Role (AWS-style):
   - Cross-account access via assumed roles
   - Temporary credentials with STS

Error Response Format

{
    "error": {
        "code": "InvalidParameter",
        "message": "The path_pattern '/api/*' conflicts with existing behavior",
        "details": {
            "field": "path_pattern",
            "conflicting_behavior_id": "cb-existing-123"
        },
        "request_id": "req-7f3a2b1c",
        "documentation_url": "https://docs.cdn.example.com/errors/InvalidParameter"
    }
}