Cloudflare Pages REST API
By the end of this module you will be able to automate Cloudflare Pages management using the REST API — listing projects, triggering deployments, managing domains, and integrating with external CI/CD systems.
Overview
The Cloudflare Pages API is part of the Cloudflare API v4 — a RESTful JSON API that lets you automate every action available in the dashboard.
Base URL:
https://api.cloudflare.com/client/v4
Pages-specific base path:
/accounts/{account_id}/pages/projects
Authentication
All API requests require authentication via either:
API Token (Recommended)
# Required permissions:
# Account > Cloudflare Pages > Edit
# Zone > Zone > Read (only if managing custom domains)
Include the token in every request:
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json"
Global API Key (Legacy — Avoid in New Projects)
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects \
-H "X-Auth-Email: your@email.com" \
-H "X-Auth-Key: your-global-api-key" \
-H "Content-Type: application/json"
Setup: Environment Variables
Set these in your shell to use all code examples below:
export CF_API_TOKEN="your-api-token"
export CF_ACCOUNT_ID="your-account-id"
export CF_PROJECT="my-docs"
Find your Account ID:
- Log in to dash.cloudflare.com
- Select any domain → right sidebar shows Account ID
- Or:
wrangler whoami
Projects API
List All Projects
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
Response:
{
"result": [
{
"id": "abc123",
"name": "my-docs",
"subdomain": "my-docs.pages.dev",
"domains": ["my-docs.pages.dev", "docs.example.com"],
"production_branch": "main",
"created_on": "2024-01-15T10:00:00.000Z",
"latest_deployment": {
"id": "deploy-xyz",
"url": "https://abc123.my-docs.pages.dev",
"environment": "production",
"created_on": "2024-04-22T05:00:00.000Z"
}
}
],
"success": true,
"errors": [],
"messages": []
}
Get a Specific Project
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
Create a New Project
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "my-docs",
"production_branch": "main"
}' | jq .
Update Project Settings
curl -X PATCH \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"deployment_configs": {
"production": {
"build_config": {
"build_command": "npm run build",
"destination_dir": "build"
},
"env_vars": {
"NODE_VERSION": {
"value": "20"
}
}
}
}
}' | jq .
Delete a Project
curl -X DELETE \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
Deployments API
List Deployments
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[] | {id, url, environment, created_on, latest_stage}'
Sample output (jq-filtered):
{
"id": "abc123def456",
"url": "https://abc123def456.my-docs.pages.dev",
"environment": "production",
"created_on": "2024-04-22T05:00:00.000Z",
"latest_stage": {
"name": "deploy",
"status": "success"
}
}
Get a Specific Deployment
DEPLOYMENT_ID="abc123def456"
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${DEPLOYMENT_ID}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
Retry a Failed Deployment
DEPLOYMENT_ID="abc123def456"
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${DEPLOYMENT_ID}/retry" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
Rollback to a Deployment
# This promotes a previous deployment to production
DEPLOYMENT_ID="previous-stable-deployment-id"
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${DEPLOYMENT_ID}/rollback" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
This is one of the most powerful API features — you can programmatically restore a previous production deployment in one API call, with no rebuild required.
Delete a Deployment
DEPLOYMENT_ID="abc123def456"
curl -X DELETE \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${DEPLOYMENT_ID}" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
Get Deployment Build Logs
DEPLOYMENT_ID="abc123def456"
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${DEPLOYMENT_ID}/history/logs" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result.logs[] | "\(.ts) \(.line)"' -r
Custom Domains API
List Custom Domains
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq '.result[] | {id, name, status, created_on}'
Response fields:
{
"id": "domain-id-123",
"name": "docs.example.com",
"status": "active",
"created_on": "2024-01-15T10:00:00.000Z"
}
Domain statuses:
| Status | Meaning |
|---|---|
initializing | DNS verification pending |
pending_cert_issuance | SSL cert being issued |
active | Live and serving traffic |
blocked | DNS conflict or verification failed |
Add a Custom Domain
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name": "docs.yourdomain.com"}' | jq .
Get Domain Status
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains/docs.yourdomain.com" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
Delete a Custom Domain
curl -X DELETE \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains/docs.yourdomain.com" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
Direct Upload via API
For programmatic deployments without a Git integration, you can upload files directly via the API.
Step 1 — Create an Upload Deployment
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"branch": "main"}' | jq .
Response includes upload_token and id for Step 2.
Step 2 — Upload Files (Multipart Form)
# Typically done via Wrangler (wraps this API):
wrangler pages deploy ./build --project-name my-docs
# Under the hood this uses:
# POST /accounts/{id}/pages/projects/{name}/uploads
# with multipart/form-data containing all build files
Direct upload via raw API is complex (requires file manifest hashing). Use wrangler pages deploy instead — it handles the entire upload process reliably.
Practical Automation Scripts
Script: Get Latest Deployment URL
#!/usr/bin/env bash
# Get the URL of the latest production deployment
LATEST_URL=$(curl -s \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq -r '.result.latest_deployment.url')
echo "Latest deployment: $LATEST_URL"
Script: Monitor Build Status
#!/usr/bin/env bash
# Poll until the latest deployment completes
PROJECT="${CF_PROJECT}"
MAX_WAIT=300 # 5 minutes
ELAPSED=0
echo "Waiting for deployment to complete..."
while [ $ELAPSED -lt $MAX_WAIT ]; do
STATUS=$(curl -s \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${PROJECT}/deployments?per_page=1" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq -r '.result[0].latest_stage.status')
echo " Status: $STATUS (${ELAPSED}s elapsed)"
if [ "$STATUS" = "success" ]; then
echo "✅ Deployment succeeded!"
exit 0
elif [ "$STATUS" = "failure" ]; then
echo "❌ Deployment failed!"
exit 1
fi
sleep 10
ELAPSED=$((ELAPSED + 10))
done
echo "⏱ Timeout: deployment did not complete in ${MAX_WAIT}s"
exit 1
Script: Purge Cache After Deployment
#!/usr/bin/env bash
# Purge Cloudflare cache after a Pages deployment
ZONE_ID="your-zone-id"
curl -X POST \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"purge_everything": true}'
Script: API Health Check (All Projects Status)
#!/usr/bin/env bash
# List all Pages projects and their last deployment status
curl -s \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq -r '.result[] | "\(.name) | \(.latest_deployment.url) | \(.latest_deployment.created_on)"'
API Response Structure
All Cloudflare API v4 responses follow this structure:
{
"result": { /* payload */ },
"success": true,
"errors": [],
"messages": [],
"result_info": {
"page": 1,
"per_page": 20,
"total_count": 42,
"count": 20
}
}
On error:
{
"result": null,
"success": false,
"errors": [
{
"code": 8000000,
"message": "An unknown error has occurred"
}
],
"messages": []
}
Rate Limits
| Plan | Requests per minute |
|---|---|
| Free | 1,200 |
| Pro | 1,200 |
| Business | 1,200 |
If you hit the rate limit, the API returns HTTP 429. Add exponential backoff to your automation scripts.
Key Takeaways
- The Cloudflare Pages API base path is
/accounts/{account_id}/pages/projects. - Authenticate with a scoped API token (
Cloudflare Pages: Edit) — not the global API key. - Key action verbs:
GET(read),POST(create/trigger),PATCH(update),DELETE(remove). - Rollback via API is a single
POSTto/deployments/{id}/rollback— no rebuild required. - Get build logs via
/deployments/{id}/history/logsfor programmatic failure detection. wrangler pages deploywraps the direct upload API — use the CLI for file uploads.
What's Next
- Continue to Build Configuration & Framework Presets to master build settings for Docusaurus and other frameworks.