// Guides2026-07-145 min read

How to Export Cloudflare Analytics to CSV

You need Cloudflare traffic numbers in a spreadsheet: for a monthly report, a board deck, or a comparison the dashboard doesn’t do.

This guide exports daily analytics to CSV with one API call and jq. It works on every Cloudflare plan.

What you’ll get

One row per day:

date,requests,bytes,cachedRequests,cachedBytes,threats,pageViews,uniques
2026-07-06,1843201,40211834112,1520339,35120993280,1203,210442,48211
...

What you need

  • curl and jq installed
  • A Cloudflare API token with Zone → Analytics → Read
  • Your zone ID (on the zone’s Overview page)
export CF_API_TOKEN="your-token"
export ZONE_ID="your-zone-id"

Step 1: Pick your date range

The daily dataset (httpRequests1dGroups) only goes back as far as your plan allows. Check your exact limit first; if you ask for older dates, the API returns an error.

START="2026-07-01"   # inclusive
END="2026-07-14"     # exclusive

Step 2: Run the export

curl -s https://api.cloudflare.com/client/v4/graphql \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data "$(jq -n --arg zone "$ZONE_ID" --arg start "$START" --arg end "$END" '{
    query: "query ($zoneTag: string, $start: Date, $end: Date) { viewer { zones(filter: {zoneTag: $zoneTag}) { httpRequests1dGroups(limit: 1000, filter: {date_geq: $start, date_lt: $end}, orderBy: [date_ASC]) { dimensions { date } sum { requests bytes cachedRequests cachedBytes threats pageViews } uniq { uniques } } } } }",
    variables: { zoneTag: $zone, start: $start, end: $end }
  }')" \
| jq -r '
  if .errors then error(.errors | tostring) else . end
  | ["date","requests","bytes","cachedRequests","cachedBytes","threats","pageViews","uniques"],
    (.data.viewer.zones[0].httpRequests1dGroups[]
      | [.dimensions.date, .sum.requests, .sum.bytes, .sum.cachedRequests,
         .sum.cachedBytes, .sum.threats, .sum.pageViews, .uniq.uniques])
  | @csv' > cloudflare-traffic.csv

Two details that save debugging time:

  • Building the request body with jq -n avoids shell-quoting bugs in the GraphQL string.
  • The if .errors check makes the command fail loudly. GraphQL returns HTTP 200 even when the query fails.

Step 3: Open it

head cloudflare-traffic.csv

Import into Google Sheets, Excel, or a database. A few useful derived columns:

Metric Formula
Cache hit ratio (requests) cachedRequests / requests
Bandwidth saved cachedBytes / bytes
GB served bytes / 1e9

Common errors

  • An error about the start date being too old — you’ve gone past your plan’s retention (notOlderThan).
  • An error about the time range being too wide — your range exceeds maxDuration. Split it into smaller windows.
  • Empty result — wrong zone ID, or the token doesn’t have access to that zone.

Why run this regularly

This export only reaches back as far as Cloudflare keeps data. If you run it once a month and append the results, the CSV becomes your own long-term history, and next year you can compare traffic year over year.

To automate it, schedule the same query in a Worker and store the results in R2.

Or skip the scripts

MetricKeeper keeps this history for you. Connect a read-only token, and it archives your Cloudflare analytics on a schedule with export built in.

Get Early Access →

Sources