The Harvest V2 API is a REST API that allows you to interact with your Harvest account programmatically. You can track time, log expenses, create projects, and more.

API Requests

Harvest requires applications to authenticate all requests with OAuth2 or Personal Access Tokens. The following HTTP methods are supported:

  • GET
  • POST
  • PATCH
  • DELETE

When issuing a GET request, parameters should be included in the URL query string:

curl https://api.harvestapp.com/v2/tasks?page=2&per_page=10 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Harvest-Account-Id: $ACCOUNT_ID" \
  -H "User-Agent: MyApp (yourname@example.com)"

When issuing a POST or PATCH request, parameters should be included in the request body. Request parameters can either be formatted in JSON or submitted as form data. When submitting request parameters as JSON, you must pass application/json in the Content-Type header.

We also require that each request include a User-Agent header with both:

  • The name of your application
  • A link to your application or email address

We use this information to get in touch if you’re doing something wrong (so we can warn you before you’re blacklisted) or something awesome (so we can congratulate you). Here are examples of acceptable User-Agent headers:

  • User-Agent: Trello (http://trello.com/contact)
  • User-Agent: John's Harvest Integration (john@example.com)

If you don’t include a User-Agent header, you’ll get a 400 Bad Request response.

JSON

curl -X POST https://api.harvestapp.com/v2/tasks \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Harvest-Account-Id: $ACCOUNT_ID" \
  -H "User-Agent: MyApp (yourname@example.com)" \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"My New Task\"}"

Form data

curl -X POST https://api.harvestapp.com/v2/tasks \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Harvest-Account-Id: $ACCOUNT_ID" \
  -H "User-Agent: MyApp (yourname@example.com)" \
  -d name="My New Task"

API Responses

Harvest formats response bodies in JSON and uses HTTP response codes to indicate the success or failure of an API request. The following are the HTTP status codes that you may encounter when using the API along with a brief explanation of when they occur.

Code Explanation
200 Your request was successful.
201 A new object has been created. It’s representation will be returned in the response body.
403 The object you requested was found but you don’t have authorization to perform your request.
404 The object you requested can’t be found.
422 There were errors processing your request. Check the response body for additional information.
429 Your request has been throttled. Refer to the Rate Limiting section for details.
500 There was a server error. Contact support@getharvest.com for help.

Rate Limiting

We have an API throttle that blocks accounts emitting more than 100 requests per 15 seconds. We reserve the right to tune the limitations, but they are always set high enough to allow a well-behaving interactive program to do its job.

For batch processes and API developers who still need to perfect their code, this throttle may be an inadvertent blocker. Just wait a bit, and try again. Since the throttle is reset with each call, the throttle will lift itself in a few minutes and API calls may resume.

When the rate limit is exceeded Harvest will send an HTTP 429 status code. The number of seconds until the throttle is lifted is sent via the Retry-After HTTP header, as specified in RFC 2616.

Please remember to write your application carefully, caching when possible. In case of abuse you may be blocked, disallowing further API access.

Conventions

When you encounter a value surrounded by curly braces, you should replace it with a value appropriate to your context. For example, if your Harvest project’s ID is 1234 and the documentation provides the following URL:

https://api.harvestapp.com/v2/projects/{PROJECT_ID}

you would replace the {PROJECT_ID} with the actual project ID, like so:

https://api.harvestapp.com/v2/projects/1234

We also make use of shell variables in our example requests (e.g. $ACCESS_TOKEN). If your operating system supports shell variables, you can set shell variables like this:

ACCESS_TOKEN="my-access-token"
ACCOUNT_ID="my-account-id"
USER_AGENT="MyApp (yourname@example.com)"

Then, you can reference them by prefixing them with a $:

curl -X POST https://api.harvestapp.com/v2/company \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Harvest-Account-Id: $ACCOUNT_ID" \
  -H "User-Agent: $USER_AGENT"

This should allow you to copy/paste most of the example requests without needing to change anything aside from IDs.

Data Types

Type Example Description
boolean true Either true or false.
string "foo" A double-quoted sequence of characters.
integer 42 A non-negative integer value.
decimal 6.8 A decimal value. The supported precision is field-dependent.
date "2017-12-31" An ISO 8601 formatted string containing just the date portion.
datetime "2017-12-31T14:59:22Z" An ISO 8601 formatted string containing a UTC date and time.
array [ 1, 2, 3 ] A JSON array.
object { name: "value" } A JSON object.