Automating Cloudflare Pages with the API
Home/Blog

Automating Cloudflare Pages with the API

3 min read446 words
cloudflare

I needed to set up staging and production domains for my blog on Cloudflare Pages. You can do this through the dashboard but I don't know, I just didn't feel like clicking around. The API is right there.

What I Wanted

Pretty straightforward:

  • eduuh.comblog-2026 main branch
  • staging.eduuh.comblog-2026 staging branch

Getting API Access

Grabbed an API token from the Cloudflare dashboard. You need:

  • Account > Cloudflare Pages > Edit
  • Account > Account Settings > Read
  • Zone > DNS > Edit (if you want to mess with DNS too)
Don't be that guy

Keep the token out of git. I stuck mine in .env and added it to .gitignore.

Adding the Domains

Attaching custom domains to a Pages project:

# Root domain
curl -X POST \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/blog-2026/domains" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{"name":"eduuh.com"}'
 
# Staging subdomain
curl -X POST \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/blog-2026/domains" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{"name":"staging.eduuh.com"}'

Making Staging Actually Point to Staging

Here's the part that tripped me up. Both domains were now attached, but they both pointed to the main branch. Not super useful for a staging environment.

You have to explicitly tell Cloudflare which branch a domain should serve:

curl -X PATCH \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/blog-2026/domains/staging.eduuh.com" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{"branch":"staging"}'

This was the key bit I couldn't figure out from the dashboard. The API made it obvious.

DNS While I'm At It

My domain's already on Cloudflare, so I figured I'd set up the DNS records via API too:

# Root domain
curl -X POST \
  "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "CNAME",
    "name": "@",
    "content": "blog-2026-290.pages.dev",
    "proxied": true
  }'
 
# Staging
curl -X POST \
  "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "CNAME",
    "name": "staging",
    "content": "blog-2026-290.pages.dev",
    "proxied": true
  }'

Both point to the same pages.dev subdomain - Cloudflare routes to the right branch based on the domain config we set earlier.

That's It

DomainBranch
eduuh.commain
staging.eduuh.comstaging

Push to main, production updates. Push to staging, staging updates.

The PATCH to map a specific branch to a domain was the thing I couldn't figure out how to do in the dashboard. Might be possible, I didn't look that hard. But the API made it obvious.


Claude figured out the APIs for me - saved me from digging through docs and clicking around the dashboard.

Last updated on January 11th, 2026