{"id":"cloudflare-api","name":"cloudflare-api","summary":"WranglerやMCPではうまく処理できない操作は、Cloudflare REST APIに直接適用してください。","body":"# Cloudflare API\n\nHit the Cloudflare REST API directly when wrangler CLI or MCP servers aren't the right tool. For bulk operations, fleet-wide changes, and features that wrangler doesn't expose.\n\n## When to Use This Instead of Wrangler or MCP\n\n| Use case | Wrangler | MCP | This skill |\n|----------|---------|-----|-----------|\n| Deploy a Worker | Yes | Yes | No |\n| Create a D1 database | Yes | Yes | No |\n| Bulk update 50 DNS records | Slow (one at a time) | Slow (one tool call each) | Yes — batch script |\n| Custom hostnames for white-label | No | Partial | Yes |\n| Email routing rules | No | Partial | Yes |\n| WAF/firewall rules | No | Yes but verbose | Yes — direct API |\n| Redirect rules in bulk | No | One at a time | Yes — batch script |\n| Zone settings across 20 zones | No | 20 separate calls | Yes — fleet script |\n| Cache purge by tag/prefix | No | Yes | Yes (when scripting) |\n| Worker route management | Limited | Yes | Yes (when bulk) |\n| Analytics/logs query | No | Partial | Yes — GraphQL |\n| D1 query/export across databases | One DB at a time | One DB at a time | Yes — cross-DB scripts |\n| R2 bulk object operations | No | One at a time | Yes — S3 API + batch |\n| KV bulk read/write/delete | One at a time | One at a time | Yes — bulk endpoints |\n| Vectorize query/delete | No | Via Worker only | Yes — direct API |\n| Queue message injection | No | Via Worker only | Yes — direct API |\n| Audit all resources in account | No | Tedious | Yes — inventory script |\n\n**Rule of thumb**: Single operations → MCP or wrangler. Bulk/fleet/scripted → API directly.\n\n## Auth Setup\n\n### API Token (recommended)\n\nCreate a scoped token at: Dashboard → My Profile → API Tokens → Create Token\n\n```bash\n# Store it\nexport CLOUDFLARE_API_TOKEN=\"your-token-here\"\n\n# Test it\ncurl -s \"https://api.cloudflare.com/client/v4/user/tokens/verify\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq '.success'\n```\n\n**Token scopes**: Always use minimal permissions. Common presets:\n- \"Edit zone DNS\" — for DNS operations\n- \"Edit zone settings\" — for zone config changes\n- \"Edit Cloudflare Workers\" — for Worker route management\n- \"Read analytics\" — for GraphQL analytics\n\n### Account and Zone IDs\n\n```bash\n# List your zones (find zone IDs)\ncurl -s \"https://api.cloudflare.com/client/v4/zones?per_page=50\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq '.result[] | {name, id}'\n\n# Get zone ID by domain name\nZONE_ID=$(curl -s \"https://api.cloudflare.com/client/v4/zones?name=example.com\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq -r '.result[0].id')\n```\n\nStore IDs in environment or a config file — don't hardcode them in scripts.\n\n## Workflows\n\n### Bulk DNS Operations\n\n**Add/update many records at once** (e.g. migrating a domain, setting up a new client):\n\n```bash\n# Pattern: read records from a file, create in batch\nwhile IFS=',' read -r type name content proxied; do\n  curl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records\" \\\n    -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\\\"type\\\":\\\"$type\\\",\\\"name\\\":\\\"$name\\\",\\\"content\\\":\\\"$content\\\",\\\"proxied\\\":$proxied,\\\"ttl\\\":1}\" \\\n    | jq '{name: .result.name, id: .result.id, success: .success}'\n  sleep 0.25  # Rate limit: 1200 req/5min\ndone < dns-records.csv\n```\n\n**Export all records from a zone** (backup or migration):\n\n```bash\ncurl -s \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  | jq -r '.result[] | [.type, .name, .content, .proxied] | @csv' > dns-export.csv\n```\n\n**Find and replace across records** (e.g. IP migration):\n\n```bash\nOLD_IP=\"203.0.113.1\"\nNEW_IP=\"198.51.100.1\"\n\n# Find records pointing to old IP\nRECORDS=$(curl -s \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?content=$OLD_IP\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq -r '.result[].id')\n\n# Update each one\nfor RECORD_ID in $RECORDS; do\n  curl -s -X PATCH \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID\" \\\n    -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\\\"content\\\":\\\"$NEW_IP\\\"}\" | jq '.success'\ndone\n```\n\n### Custom Hostnames (White-Label Client Domains)\n\nFor SaaS apps where clients use their own domain (e.g. `app.clientdomain.com` → your Worker):\n\n```bash\n# Create custom hostname\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_hostnames\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"hostname\": \"app.clientdomain.com\",\n    \"ssl\": {\n      \"method\": \"http\",\n      \"type\": \"dv\",\n      \"settings\": {\n        \"min_tls_version\": \"1.2\"\n      }\n    }\n  }' | jq '{id: .result.id, status: .result.status, ssl_status: .result.ssl.status}'\n\n# List custom hostnames\ncurl -s \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_hostnames?per_page=50\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  | jq '.result[] | {hostname, status, ssl_status: .ssl.status}'\n\n# Check status (client needs to add CNAME)\ncurl -s \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_hostnames/$HOSTNAME_ID\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq '.result.status'\n```\n\n**Client setup**: They add a CNAME: `app.clientdomain.com → your-worker.your-domain.com`\n\n### Email Routing Rules\n\n```bash\n# Enable email routing on zone\ncurl -s -X PUT \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/enable\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\"\n\n# Create a routing rule (forward info@ to a real address)\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Forward info@\",\n    \"enabled\": true,\n    \"matchers\": [{\"type\": \"literal\", \"field\": \"to\", \"value\": \"info@example.com\"}],\n    \"actions\": [{\"type\": \"forward\", \"value\": [\"real-inbox@gmail.com\"]}]\n  }' | jq '.success'\n\n# Create catch-all rule\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Catch-all\",\n    \"enabled\": true,\n    \"matchers\": [{\"type\": \"all\"}],\n    \"actions\": [{\"type\": \"forward\", \"value\": [\"catchall@company.com\"]}]\n  }' | jq '.success'\n\n# List rules\ncurl -s \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq '.result[] | {name, enabled, matchers, actions}'\n```\n\n### Cache Purge\n\n```bash\n# Purge everything (nuclear option)\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"purge_everything\": true}'\n\n# Purge specific URLs\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"files\": [\"https://example.com/styles.css\", \"https://example.com/app.js\"]}'\n\n# Purge by cache tag (requires Enterprise or cache tag headers)\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"tags\": [\"product-123\", \"homepage\"]}'\n\n# Purge by prefix\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prefixes\": [\"https://example.com/images/\"]}'\n```\n\n### Redirect Rules (Bulk)\n\n```bash\n# Create a redirect rule\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_dynamic_redirect/entrypoint\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rules\": [\n      {\n        \"expression\": \"(http.request.uri.path eq \\\"/old-page\\\")\",\n        \"description\": \"Redirect old-page to new-page\",\n        \"action\": \"redirect\",\n        \"action_parameters\": {\n          \"from_value\": {\n            \"target_url\": {\"value\": \"https://example.com/new-page\"},\n            \"status_code\": 301\n          }\n        }\n      }\n    ]\n  }'\n```\n\n**For bulk redirects** (301s from a CSV), generate the rules array programmatically:\n\n```python\nimport json, csv\n\nrules = []\nwith open('redirects.csv') as f:\n    for row in csv.reader(f):\n        old_path, new_url = row\n        rules.append({\n            \"expression\": f'(http.request.uri.path eq \"{old_path}\")',\n            \"description\": f\"Redirect {old_path}\",\n            \"action\": \"redirect\",\n            \"action_parameters\": {\n                \"from_value\": {\n                    \"target_url\": {\"value\": new_url},\n                    \"status_code\": 301\n                }\n            }\n        })\nprint(json.dumps({\"rules\": rules}, indent=2))\n```\n\n### Zone Settings (Fleet-Wide)\n\nApply the same settings across multiple zones:\n\n```bash\n# Settings to apply\nSETTINGS='{\"value\":\"full\"}'  # SSL mode: full (strict)\n\n# Get all active zones\nZONES=$(curl -s \"https://api.cloudflare.com/client/v4/zones?status=active&per_page=50\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq -r '.result[].id')\n\n# Apply to each zone\nfor ZONE in $ZONES; do\n  curl -s -X PATCH \"https://api.cloudflare.com/client/v4/zones/$ZONE/settings/ssl\" \\\n    -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"$SETTINGS\" | jq \"{zone: .result.id, success: .success}\"\n  sleep 0.25\ndone\n```\n\nCommon fleet settings:\n- `ssl` — \"full\" or \"strict\"\n- `min_tls_version` — \"1.2\"\n- `always_use_https` — \"on\"\n- `security_level` — \"medium\"\n- `browser_cache_ttl` — 14400\n\n### WAF / Firewall Rules\n\n```bash\n# Create a WAF custom rule (block by country)\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_firewall_custom/entrypoint\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rules\": [{\n      \"expression\": \"(ip.geoip.country in {\\\"RU\\\" \\\"CN\\\"})\",\n      \"action\": \"block\",\n      \"description\": \"Block traffic from RU and CN\"\n    }]\n  }'\n\n# Rate limiting rule\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_ratelimit/entrypoint\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rules\": [{\n      \"expression\": \"(http.request.uri.path contains \\\"/api/\\\")\",\n      \"action\": \"block\",\n      \"ratelimit\": {\n        \"characteristics\": [\"ip.src\"],\n        \"period\": 60,\n        \"requests_per_period\": 100\n      },\n      \"description\": \"Rate limit API to 100 req/min per IP\"\n    }]\n  }'\n```\n\n### Worker Routes\n\n```bash\n# List routes\ncurl -s \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/workers/routes\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" | jq '.result[] | {pattern, id}'\n\n# Create route\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/workers/routes\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"pattern\": \"api.example.com/*\", \"script\": \"my-worker\"}'\n\n# Delete route\ncurl -s -X DELETE \"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/workers/routes/$ROUTE_ID\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\"\n```\n\n### Analytics (GraphQL)\n\n```bash\n# Worker analytics (requests, errors, CPU time)\ncurl -s -X POST \"https://api.cloudflare.com/client/v4/graphql\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\": \"{ viewer { zones(filter: {zoneTag: \\\"'$ZONE_ID'\\\"}) { httpRequests1dGroups(limit: 7, filter: {date_gt: \\\"2026-03-10\\\"}) { dimensions { date } sum { requests pageViews } } } } }\"\n  }' | jq '.data.viewer.zones[0].httpRequests1dGroups'\n```\n\n## Rate Limits\n\n| Endpoint | Limit |\n|---------|-------|\n| Most API calls | 1200 requests / 5 minutes |\n| DNS record operations | 1200 / 5 min (shared with above) |\n| Cache purge | 1000 purge calls / day |\n| Zone creation | 5 per minute |\n\n**In scripts**: Add `sleep 0.25` between calls for sustained operations. Use `p-limit` or `xargs -P 4` for controlled parallelism.\n\n## Script Generation\n\nWhen the user describes what they need, generate a script in `.jez/scripts/` that:\n- Reads API token from environment (never hardcode)\n- Handles pagination for list operations\n- Includes error checking (`jq '.success'` after each call)\n- Adds rate limit sleep between calls\n- Logs what it does\n- Supports `--dry-run` where possible\n\nPrefer `curl` + `jq` for simple operations. Use Python for complex logic (pagination loops, error handling, CSV processing). Use TypeScript with the `cloudflare` npm package for type safety in larger scripts.\n\n## API Reference\n\nBase URL: `https://api.cloudflare.com/client/v4/`\n\nFull docs: `https://developers.cloudflare.com/api/`\n\nThe API follows a consistent pattern:\n- `GET /zones` — list\n- `POST /zones` — create\n- `GET /zones/:id` — read\n- `PATCH /zones/:id` — update\n- `DELETE /zones/:id` — delete\n- `PUT /zones/:id/settings/:name` — update setting\n\nEvery response has `{ success: bool, errors: [], messages: [], result: {} }`.\n\n## Reference Files\n\n| When | Read |\n|------|------|\n| D1, R2, KV, Workers, Vectorize, Queues API patterns | [references/developer-platform-api.md](references/developer-platform-api.md) |","author":"@jezweb","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/jezweb/claude-skills/tree/main/plugins/cloudflare/skills/cloudflare-api","license":"MIT","category":"writing","lang":"en","tokens":3900,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"references/developer-platform-api.md","size":12252,"sha256":"4ac9a97d9c12f77e0a33424b70f902cbcb7fdb9684fe54f245857cc34b28c31d"}],"requires":{"mcp":[],"tools":["Read","Write","Edit","Bash","Glob","Grep"]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["api.cloudflare.com","developers.cloudflare.com"]}}