Pulling Payroll Reports
Use report runs to generate payroll journal and payroll summary reports over the Check API
A report run is one payroll report requested over the Check API. You create a report run, Check generates the report, and you download the finished file once the run completes. This is how you pull a payroll journal or a payroll summary over the API.
Report runs support two reports:
payroll_journal— lists employees' earnings, deductions, and taxes by payroll for a payday range.payroll_summary— lists employees' earnings, deductions, and taxes aggregated over a payday range.
The report run lifecycle
After creation, a report run moves from generating to completed or failed:
generating— the run was created and Check is building the report.completed— the report finished generating and can be downloaded fromresult.url.failed— the report could not be generated.
Report generation may take up to 20 minutes, though most reports complete much faster. Data that report runs rely on may be up to 10 minutes out of date, so a payroll approved moments ago may not appear in a report run created immediately afterward.
Step 1: Create a report run
Create a report run by POSTing to /report_runs with the report type and its parameters. Scope the run to the company and the payday range you need:
curl -X POST https://api.checkhq.com/report_runs \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: 9f2b8c1e-payroll-journal-2025-q1" \
-d '{
"report": "payroll_journal",
"company": "com_sx3svU6K8c5ZkSFlOh5p",
"parameters": {
"payday_from": "2025-01-01",
"payday_to": "2025-03-31",
"contractors": true,
"additional_columns": ["employee.id", "payroll.id"]
}
}'The response is a report run in the generating status:
{
"id": "run_Bw6EkMDmxCPeVQ2eYzLu",
"report": "payroll_journal",
"status": "generating",
"company": "com_sx3svU6K8c5ZkSFlOh5p",
"created_at": "2026-06-24T18:26:56.848920Z",
"completed_at": null,
"parameters": {
"payday_from": "2025-01-01",
"payday_to": "2025-03-31"
},
"result": null,
"metadata": {}
}- Always supply an
X-Idempotency-Keyheader. If your create request fails with a network error, you can safely retry with the same key without creating a duplicate report run. - Include
additional_columnslikeemployee.idorpayroll.idwhen you plan to join the report output against records in your own system. Matching rows by ID is reliable; matching by employee name is not.employee.metadata.<key>columns carry through keys you store on Check employee records, such as your own internal employee identifiers. - Use
metadatato tag runs with identifiers from your own system (for example, the internal request or customer ID that triggered the report) so you can correlate runs when listing them later.
Respect the rate limits
Report run creation is rate limited to 1 request per second per partner, with a maximum of 20 report runs generating at once. If you generate reports on a schedule, for example quarter-end journals for every customer, queue the create requests on your side and submit them serially rather than fanning out in parallel. A run leaves the concurrency pool when it reaches completed or failed. Create the next run as each one finishes, and you stay at maximum throughput without hitting either limit.
Step 2: Wait for the run to complete
Poll Get a report run until status is completed or failed:
curl https://api.checkhq.com/report_runs/run_Bw6EkMDmxCPeVQ2eYzLu \
-H "Authorization: Bearer {api_key}"Poll at a relaxed interval, every 30 to 60 seconds, and use a backoff rather than a tight loop. You can also listen for report_run webhooks to be notified when the run's status changes, which avoids polling entirely.
If the run reaches the failed status, create a new run to retry. When the run covers a wide payday range, split the range into smaller windows before retrying.
Step 3: Download the report
Once the run is completed, the run's result.url points to the download endpoint. Calling it returns a short-lived presigned URL for the generated artifact:
curl https://api.checkhq.com/report_runs/run_Bw6EkMDmxCPeVQ2eYzLu/download \
-H "Authorization: Bearer {api_key}"{
"download_url": "https://...",
"extension": "zip",
"content_type": "application/zip"
}Download the file from download_url promptly, since presigned URLs are short-lived. You can call the download endpoint again to get a fresh one. Report artifacts also expire: runs whose artifacts have expired are omitted from list results and can no longer be downloaded. If you need to retain reports long-term, store the downloaded artifact in your own system rather than relying on re-downloading from Check.
Scoping and tracking your runs
Split a wide payday range into windows
Rather than requesting a full year of payroll journal data in one run, create one run per month or per quarter. Smaller windows generate faster, produce smaller artifacts, and let you retry a single window if a run fails, instead of regenerating the entire year. Since paydays are the boundary (payday_from and payday_to are both inclusive), make sure consecutive windows don't overlap: 2025-01-01 to 2025-03-31, then 2025-04-01 to 2025-06-30.
You can create runs for multiple windows concurrently, up to the 20-run concurrency limit, and download each as it completes.
Scope a run to a single payroll
When you only need data for one payroll, for example to reconcile a specific pay period, pass the payroll parameter. The payday range is still required, so keep it to the window that contains that payroll's payday. This keeps generation time and artifact size to a minimum.
Track your runs with list filters
List report runs supports filtering by company, report_type, and status. When orchestrating many runs across many companies, filter by status=generating to see what's still in flight, or by company to find the latest completed run for a given customer.
Updated 2 days ago

