How Do I Export GitHub Actions Job Data to CSV?
Every WarpBuild report tab exports CSV with all filtered rows, and the same five reports return CSV from the API with format=csv for a scheduled pull.
Answer
GitHub Actions job data exports to CSV from the download button on every report tab in the WarpBuild dashboard, and the file carries all rows matching the current filters and sort order rather than the page on screen, as described in the reports documentation. The same five reports return CSV from the reports API when the request carries format=csv, which is the path to take when the export has to run on a schedule rather than by hand.
Five exports exist, and each one is keyed differently. Billing has three tabs: CI, one row per job execution; Docker Builder, one row per builder session; and Cache, one row per cache billing entry. Jobs aggregates to one row per repository, workflow, and job name. Queue Timings aggregates to one row per runner label and stack pair. Picking the wrong one is the usual reason an export looks short: a month of 12,000 job executions is 12,000 rows in the CI export and perhaps 40 rows in the Jobs export, because Jobs has already rolled the runs up.
Detail
What each export carries
Column names below are the field names the reports API returns, and the dashboard export uses the same set.
| Export | One row is | Columns |
|---|---|---|
Billing, CI tab (/reports/billing/ci) | one job execution | repo, job_name, runner_label, stack, stack_kind, run_id, vcs_job_id, timestamp, execution_time, billed_time, runner_cost, snapshot, snapshot_cost, total_cost |
Billing, Docker Builder tab (/reports/billing/docker-builder) | one builder session | profile, arch, duration, cost, timestamp |
Billing, Cache tab (/reports/billing/cache) | one cache billing entry | type, cost, timestamp |
Jobs (/reports/jobs) | one repository, workflow, and job name | repo, workflow_name, job_name, run_count, success_rate, duration_p75, duration_p90, queue_time_p75, queue_time_p90, cpu_p75, cpu_p90, memory_p75, memory_p90 |
Queue Timings (/reports/queue-timings) | one runner label and stack pair | runner_label, stack, stack_kind, run_count, queue_time_p75, queue_time_p90 |
Three details decide whether a downstream analysis works. The CI export carries vcs_job_id and run_id, which are the GitHub Actions job id and workflow run id, so the rows join to anything already keyed on a GitHub Actions run. Snapshot runners bill inside the CI row through snapshot and snapshot_cost, while remote Docker builders bill on their own tab, so a workflow whose image build moved to a builder splits across two exports. And cpu_p75 through memory_p90 come from CI observability; jobs without telemetry show a dash in the Jobs export, per the reports documentation.
The scheduled path
The API lives at https://api.warpbuild.com/api/v1 and takes an API key that starts with wkey- on the ci scope, sent as an Authorization: Bearer header, the same key type used across the automation endpoints. start_date and end_date are required RFC3339 timestamps. Everything else is optional: repos, runner_labels, stack_ids, job_names, vcs_job_ids, run_ids, snapshot, search, sort_by, and sort_order.
curl -sS -G 'https://api.warpbuild.com/api/v1/reports/billing/ci' \
-H 'Authorization: Bearer wkey-xxxx' \
--data-urlencode 'start_date=2026-07-01T00:00:00Z' \
--data-urlencode 'end_date=2026-08-01T00:00:00Z' \
--data-urlencode 'chart_group_by=runner_label' \
--data-urlencode 'format=csv' \
-o ci-billing-2026-07.csvDrop format=csv and the same call returns JSON with a jobs.items array, paginated at 50 rows by default and 200 at most. Wrapping the CSV call in a monthly workflow takes a small runner and one artifact upload.
name: monthly-actions-job-export
on:
schedule:
- cron: "0 6 1 * *"
workflow_dispatch:
jobs:
export:
runs-on: warp-ubuntu-latest-x64-2x
steps:
- name: Compute last month in UTC
id: window
run: |
echo "start=$(date -u -d 'last month' +%Y-%m-01T00:00:00Z)" >> "$GITHUB_OUTPUT"
echo "end=$(date -u +%Y-%m-01T00:00:00Z)" >> "$GITHUB_OUTPUT"
- name: Download the job rows
run: |
curl -sS -G 'https://api.warpbuild.com/api/v1/reports/billing/ci' \
-H "Authorization: Bearer ${{ secrets.WARPBUILD_API_KEY }}" \
--data-urlencode "start_date=${{ steps.window.outputs.start }}" \
--data-urlencode "end_date=${{ steps.window.outputs.end }}" \
--data-urlencode 'format=csv' \
-o ci-billing.csv
- name: Roll minutes up by repository and label
run: |
curl -sS -G 'https://api.warpbuild.com/api/v1/reports/billing/ci' \
-H "Authorization: Bearer ${{ secrets.WARPBUILD_API_KEY }}" \
--data-urlencode "start_date=${{ steps.window.outputs.start }}" \
--data-urlencode "end_date=${{ steps.window.outputs.end }}" \
--data-urlencode 'per_page=200' \
| jq -r '.jobs.items
| group_by(.repo + "|" + .runner_label)[]
| [ .[0].repo, .[0].runner_label, length,
(map(.billed_time) | add), (map(.total_cost) | add) ]
| @tsv' | tee by-repo-and-label.tsv
- uses: actions/upload-artifact@v4
with:
name: actions-job-export
path: |
ci-billing.csv
by-repo-and-label.tsvKeep the window boundaries on UTC midnight so consecutive months neither overlap nor leave a gap, and walk page until jobs.next stops advancing when a busy month exceeds 200 rows on the JSON path.
A worked rollup: minutes by label by repository
Take one month of the CI export, group the rows by repo and runner_label, sum billed_time per group and convert it to minutes, then multiply by the published rate for that label. Use billed_time rather than execution_time, because billed time is the figure that reconciles against an invoice. Rates come from the WarpBuild pricing page and were checked on 2026-08-13; billing is per minute.
| Repository | Runner label | Billed minutes | Rate | Cost |
|---|---|---|---|---|
platform-api | warp-ubuntu-latest-x64-4x | 18,400 | $0.008 | $147.20 |
platform-api | warp-ubuntu-latest-x64-16x | 3,100 | $0.032 | $99.20 |
web-app | warp-ubuntu-latest-x64-2x | 9,700 | $0.004 | $38.80 |
web-app | warp-ubuntu-latest-arm64-4x | 5,200 | $0.006 | $31.20 |
ios-client | warp-macos-latest-arm64-6x | 1,450 | $0.08 | $116.00 |
| Total | 37,850 | $432.40 |
Two readings fall straight out of that table. The ios-client repository holds 1,450 of 37,850 billed minutes, which is 3.8 percent of the fleet's minutes and $116.00 of $432.40, or 26.8 percent of the money, so a macOS review pass moves more than a Linux one. And the 16 vCPU line on platform-api holds 3,100 minutes against 18,400 on the 4 vCPU label, yet costs two thirds as much, which is the row to check against cpu_p75 in the Jobs export before renewing its label for another quarter.
Every one of them appears in the runner_label column, so a single export covers the whole fleet rather than one platform at a time. The cost attribution guide turns the same grouping into a chargeback ledger, and GitHub Actions observability covers the reports these exports come from.
What the export will not tell you
The rows carry usage, and usage is the whole bill. The pricing page carries the full rate table these exports bill against.
Related Questions
Does the CSV export contain only the rows visible on screen?
The export contains every row matching the current filters and sort order rather than the current page, so a month with 12,000 job executions arrives as 12,000 rows in one file. Set the filters and the sort first, then download, because the export inherits both. The reports documentation lists the filters available on each tab.
Which columns does the GitHub Actions job export carry?
The CI billing export carries one row per job execution with repo, job_name, runner_label, stack, stack_kind, run_id, vcs_job_id, timestamp, execution_time, billed_time, runner_cost, snapshot, snapshot_cost, and total_cost. Percentiles for duration, queue time, CPU, and memory live in the Jobs export instead, which is the file to pull when the question is per-workflow rather than per-run. The cost per workflow answer covers that split.
Can the CSV export run on a schedule instead of a download button?
Yes. The reports API takes the same parameters as the dashboard and returns the same rows when the request carries format=csv, so a scheduled workflow with a wkey- API key on the ci scope can write the file into an artifact, a bucket, or a warehouse load on the first of every month. The weekly health report guide wires the same pull into a recurring summary, and the automation documentation covers creating the key.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.