Detailed Reports

Detailed reports return entry-level time and expense rows for a report’s filters, together with the report’s own totals. Each response contains an object with a results property that contains an array of up to per_page rows. If no more rows are available, the resulting array will be empty. Several additional pagination properties are included in the response to simplify paginating your results.

An entry that has recorded no hours is not a row: it is absent from results and total_entries, and its zero is not in totals. A running timer records zero hours until it stops, so it is not a row either. An entry whose project, task, client or user no longer resolves is likewise not a row, and the totals are summed over exactly the rows.

The time report row object

Attribute Type Description
id integer Unique ID for the time entry.
spent_date date Date the time was tracked.
hours decimal Hours recorded on the entry. A running timer is not included. totals.total_hours sums this field.
billable boolean Whether the entry is billable.
is_billed boolean Whether the entry has been invoiced.
approval_status string Approval status of the entry: unsubmitted, submitted, or approved.
notes string Notes on the entry.
client_id integer The ID of the client associated with the entry.
client_name string The name of the client associated with the entry.
project_id integer The ID of the project associated with the entry.
project_name string The name of the project associated with the entry. Includes the project code.
task_id integer The ID of the task associated with the entry.
task_name string The name of the task associated with the entry.
user_id integer The ID of the user associated with the entry.
user_name string The name of the user associated with the entry.

The expense report row object

Attribute Type Description
id integer Unique ID for the expense.
spent_date date Date the expense was incurred.
total_cost decimal Total cost of the expense.
units decimal Number of units on the expense.
currency string Currency code of the expense.
billable boolean Whether the expense is billable.
is_billed boolean Whether the expense has been invoiced.
approval_status string Approval status of the expense: unsubmitted, submitted, or approved.
notes string Notes on the expense.
client_id integer The ID of the client associated with the expense.
client_name string The name of the client associated with the expense.
project_id integer The ID of the project associated with the expense.
project_name string The name of the project associated with the expense. Includes the project code.
expense_category_id integer The ID of the expense category associated with the expense.
expense_category_name string The name of the expense category associated with the expense.
user_id integer The ID of the user associated with the expense.
user_name string The name of the user associated with the expense.

Required permissions

For the time report, Administrators see all time entries for the account. Managers see their own entries, plus entries for the projects and people they manage. Members see only their own entries.

For the expense report, Administrators see all expenses for the account. Managers see their own expenses, plus expenses for the projects and people they manage. Members see only their own expenses.

Filtering by is_billed requires permission to read billed state: the time report requires permission to read any invoices, and the expense report requires permission to read all expenses. A caller without that permission receives a 403 Forbidden response code when they use the filter.

Time Detailed Report

Beta endpoint. Fully available, but the response shape may still change. Until it is frozen, every response includes Harvest-Api-Stability: beta.

GET /v2/reports/time/detailed
Parameter Type Description
from date Required. Only report on time entries with a spent_date on or after the given date.
to date Required. Only report on time entries with a spent_date on or before the given date. The timeframe cannot exceed 365 days.
client_id integer Only report on time entries for the given client.
project_id integer Only report on time entries for the given project.
task_id integer Only report on time entries for the given task.
user_id integer Only report on time entries for the given user.
is_billed string Pass true for invoiced entries and false for uninvoiced entries.
approval_status string Only report on entries with the given approval status: unsubmitted, submitted, or approved.
group_by string The grouping to order rows by: dates (default), clients, projects, tasks, or users.
page integer The page number to use in pagination. (Default: 1)
per_page integer The number of records to return per page. Can range between 1 and 2000. (Default: 2000)

Example Request:

curl "https://api.harvestapp.com/v2/reports/time/detailed?from=2026-09-01&to=2026-09-30&group_by=projects" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Harvest-Account-Id: $ACCOUNT_ID" \
  -H "User-Agent: MyApp ([email protected])"

Example Response:

{
  "results": [
    {
      "id": 636709355,
      "spent_date": "2026-09-22",
      "hours": 3.0,
      "billable": true,
      "is_billed": false,
      "approval_status": "approved",
      "notes": "Design review",
      "client_id": 5735774,
      "client_name": "ABC Corp",
      "project_id": 14307913,
      "project_name": "[MW] Marketing Website",
      "task_id": 8083365,
      "task_name": "Graphic Design",
      "user_id": 1782959,
      "user_name": "Kim Allen"
    },
    {
      "id": 636709360,
      "spent_date": "2026-09-24",
      "hours": 5.5,
      "billable": false,
      "is_billed": false,
      "approval_status": "submitted",
      "notes": "Team meeting",
      "client_id": 5735774,
      "client_name": "ABC Corp",
      "project_id": 14307913,
      "project_name": "[MW] Marketing Website",
      "task_id": 8083368,
      "task_name": "Project Management",
      "user_id": 1782884,
      "user_name": "Bob Powell"
    }
  ],
  "totals": {
    "total_hours": 8.5,
    "billable_hours": 3.0
  },
  "per_page": 2000,
  "total_pages": 1,
  "total_entries": 2,
  "next_page": null,
  "previous_page": null,
  "page": 1,
  "links": {
    "first": "https://api.harvestapp.com/v2/reports/time/detailed?from=2026-09-01&group_by=projects&page=1&per_page=2000&to=2026-09-30",
    "next": null,
    "previous": null,
    "last": "https://api.harvestapp.com/v2/reports/time/detailed?from=2026-09-01&group_by=projects&page=1&per_page=2000&to=2026-09-30"
  }
}

Expense Detailed Report

Beta endpoint. Fully available, but the response shape may still change. Until it is frozen, every response includes Harvest-Api-Stability: beta.

GET /v2/reports/expenses/detailed
Parameter Type Description
from date Required. Only report on expenses with a spent_date on or after the given date.
to date Required. Only report on expenses with a spent_date on or before the given date. The timeframe cannot exceed 365 days.
client_id integer Only report on expenses for the given client.
project_id integer Only report on expenses for the given project.
expense_category_id integer Only report on expenses for the given expense category.
user_id integer Only report on expenses for the given user.
is_billed string Pass true for invoiced expenses and false for uninvoiced expenses.
approval_status string Only report on expenses with the given approval status: unsubmitted, submitted, or approved.
group_by string The grouping to order rows by: dates (default), clients, projects, categories, or users.
page integer The page number to use in pagination. (Default: 1)
per_page integer The number of records to return per page. Can range between 1 and 2000. (Default: 2000)

Example Request:

curl "https://api.harvestapp.com/v2/reports/expenses/detailed?from=2026-09-01&to=2026-09-30&group_by=categories" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Harvest-Account-Id: $ACCOUNT_ID" \
  -H "User-Agent: MyApp ([email protected])"

Example Response:

{
  "results": [
    {
      "id": 15296442,
      "spent_date": "2026-09-18",
      "total_cost": 133.35,
      "units": 1.0,
      "currency": "USD",
      "billable": true,
      "is_billed": false,
      "approval_status": "submitted",
      "notes": "Client dinner",
      "client_id": 5735774,
      "client_name": "ABC Corp",
      "project_id": 14307913,
      "project_name": "[MW] Marketing Website",
      "expense_category_id": 4195926,
      "expense_category_name": "Meals",
      "user_id": 1782959,
      "user_name": "Kim Allen"
    }
  ],
  "totals": [
    {
      "currency": "USD",
      "total_amount": 133.35
    }
  ],
  "per_page": 2000,
  "total_pages": 1,
  "total_entries": 1,
  "next_page": null,
  "previous_page": null,
  "page": 1,
  "links": {
    "first": "https://api.harvestapp.com/v2/reports/expenses/detailed?from=2026-09-01&group_by=categories&page=1&per_page=2000&to=2026-09-30",
    "next": null,
    "previous": null,
    "last": "https://api.harvestapp.com/v2/reports/expenses/detailed?from=2026-09-01&group_by=categories&page=1&per_page=2000&to=2026-09-30"
  }
}

The expense totals are returned per currency and are never summed across currencies.

Errors

Status When
400 from or to is missing: Cannot complete your request. Parameter is missing or the value is empty: from.
400 A date is malformed: Invalid from date provided: "...".
400 The timeframe exceeds 365 days: Invalid timeframe: must not exceed 1 year.
400 An unknown approval_status: Invalid approval_status: must be one of unsubmitted, submitted, approved.
400 An unknown group_by: Invalid group_by: must be one of dates, clients, projects, tasks, users (the expense report lists ... projects, categories, users).
400 An invalid is_billed: Invalid is_billed: must be true or false.
400 An ID is not an integer, for example Invalid client_id integer provided: "any".
400 An ID is <= 0, for example Invalid project_id: must be greater than 0.
400 page or per_page is malformed or out of range.
401 Missing or invalid credentials.
403 The caller cannot filter by billed state: You do not have permission to filter by billed status.
403 The caller cannot read reports: Not authorized!.
403 The account is inactive: Account inactive.

Still have questions? We’re happy to help!

Contact Us