Hourtick Time API

Timers and time entries. Link time to a task with `workItemId` on start, create or edit.

Operations 4

GET /api/v1/time-entries Your time entries in a date range, plus the running entry #
POST /api/v1/commands Start, stop, continue, create, edit, delete or restore time #
GET /api/v1/stream Server-Sent Events: live entry changes for your account and task changes in the… #
GET /api/v1/activity Your day: tracked entries and what you did (tasks created, moved or commented… #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hourtick:hourtick-time-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

hourtick-time-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hourtick Time API
  version: 1.0.0
  description: 'The Hourtick REST API. Authenticate with a personal API token: create one in **Settings → API tokens**, then send `Authorization: Bearer ht_…`.


    All timer changes go through **POST /api/v1/commands**, the same endpoint the web and Mac apps use. Commands are idempotent per `commandId` (use a new UUID per action, reuse it to retry safely).


    Every response carries a `Server-Time` header (epoch ms). Errors use RFC 9457 problem details.'
servers:
- url: https://hourtick.com
security:
- bearer: []
tags:
- name: Time
  description: Timers and time entries. Link time to a task with `workItemId` on start, create or edit.
paths:
  /api/v1/time-entries:
    get:
      tags:
      - Time
      summary: Your time entries in a date range, plus the running entry
      parameters:
      - name: from
        in: query
        required: true
        schema:
          type: string
          format: date
      - name: to
        in: query
        required: true
        schema:
          type: string
          format: date
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries:
                    type: array
                    items:
                      $ref: '#/components/schemas/TimeEntry'
                  running:
                    oneOf:
                    - $ref: '#/components/schemas/TimeEntry'
                    - type: 'null'
        '401':
          description: Missing or invalid token
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '422':
          description: Invalid request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      operationId: getApiV1TimeEntries
      x-operation-id-source: derived
  /api/v1/commands:
    post:
      tags:
      - Time
      summary: Start, stop, continue, create, edit, delete or restore time
      description: '`command.type`: `start` (stops any running timer), `restart` (continue an entry on its own date), `stop`, `create` (finished entry with a duration), `edit` (needs `expectedVersion`), `delete`, `restore`. Locked entries return 423; stale versions return 409 with the current entry.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                commandId:
                  type: string
                  format: uuid
                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                command:
                  oneOf:
                  - type: object
                    properties:
                      type:
                        type: string
                        const: start
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      spentDate:
                        type: string
                      projectId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      taskId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      workItemId:
                        anyOf:
                        - type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        - type: 'null'
                      notes:
                        type: string
                        maxLength: 5000
                      billable:
                        type: boolean
                    required:
                    - type
                    - entryId
                    - projectId
                    - taskId
                    - notes
                    - billable
                  - type: object
                    properties:
                      type:
                        type: string
                        const: restart
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    required:
                    - type
                    - entryId
                  - type: object
                    properties:
                      type:
                        type: string
                        const: stop
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    required:
                    - type
                    - entryId
                  - type: object
                    properties:
                      type:
                        type: string
                        const: edit
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      expectedVersion:
                        type: integer
                        minimum: 1
                        maximum: 9007199254740991
                      patch:
                        type: object
                        properties:
                          projectId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          taskId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          workItemId:
                            anyOf:
                            - type: string
                              format: uuid
                              pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                            - type: 'null'
                          notes:
                            type: string
                            maxLength: 5000
                          billable:
                            type: boolean
                          spentDate:
                            type: string
                          durationSeconds:
                            type: integer
                            minimum: 0
                            maximum: 86400
                        additionalProperties: false
                    required:
                    - type
                    - entryId
                    - expectedVersion
                    - patch
                  - type: object
                    properties:
                      type:
                        type: string
                        const: delete
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      expectedVersion:
                        type: integer
                        minimum: 1
                        maximum: 9007199254740991
                    required:
                    - type
                    - entryId
                    - expectedVersion
                  - type: object
                    properties:
                      type:
                        type: string
                        const: restore
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    required:
                    - type
                    - entryId
                  - type: object
                    properties:
                      type:
                        type: string
                        const: create
                      entryId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      spentDate:
                        type: string
                      durationSeconds:
                        type: integer
                        minimum: 0
                        maximum: 86400
                      projectId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      taskId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      workItemId:
                        anyOf:
                        - type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        - type: 'null'
                      notes:
                        type: string
                        maxLength: 5000
                      billable:
                        type: boolean
                    required:
                    - type
                    - entryId
                    - spentDate
                    - durationSeconds
                    - projectId
                    - taskId
                    - notes
                    - billable
                clientTime:
                  type: integer
                  exclusiveMinimum: 0
                  maximum: 9007199254740991
              required:
              - commandId
              - command
            example:
              commandId: 0192f1a0-7c1e-7cc0-9b8e-2f5f0b0a1c11
              command:
                type: start
                entryId: 0192f1a0-7c1e-7cc0-9b8e-2f5f0b0a1c12
                projectId: <project uuid>
                taskId: <task uuid>
                notes: Homepage copy
                billable: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries:
                    type: array
                    items:
                      $ref: '#/components/schemas/TimeEntry'
                  noop:
                    type: boolean
        '401':
          description: Missing or invalid token
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '409':
          description: Version conflict (response includes the current entry)
        '422':
          description: Invalid request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '423':
          description: Entry or day is locked (invoiced, submitted or approved)
      operationId: postApiV1Commands
      x-operation-id-source: derived
  /api/v1/stream:
    get:
      tags:
      - Time
      summary: 'Server-Sent Events: live entry changes for your account and task changes in the…'
      responses:
        '200':
          description: 'text/event-stream with `hello`, `entries`, `work_items` (`workItemId`), `chat` (`channelId`, `messageId`, `parentId`, `change`: message, edit, delete, reaction, read, channel, typing or agent) and `presence` events. Private channels and DMs only reach their members'
      operationId: getApiV1Stream
      x-operation-id-source: derived
  /api/v1/activity:
    get:
      tags:
      - Time
      summary: 'Your day: tracked entries and what you did (tasks created, moved or commented…'
      parameters:
      - name: date
        in: query
        schema:
          type: string
          format: date
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: getApiV1Activity
      x-operation-id-source: derived
components:
  schemas:
    Problem:
      type: object
      properties:
        type:
          type: string
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        entry:
          $ref: '#/components/schemas/TimeEntry'
        conflicts:
          type: array
          description: 'Bulk edits: entries that blocked the change.'
          items:
            type: object
            properties:
              id:
                type: string
              reason:
                type: string
    TimeEntry:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspaceId:
          type: string
          format: uuid
        userId:
          type: string
        projectId:
          type: string
          format: uuid
        taskId:
          type: string
          format: uuid
          description: Task type
        workItemId:
          type:
          - string
          - 'null'
          format: uuid
          description: The task this time is for, if any
        notes:
          type: string
        spentDate:
          type: string
          format: date
          description: The day the time belongs to (user's time zone).
        timezone:
          type: string
          examples:
          - Europe/Copenhagen
        accumulatedSeconds:
          type: integer
          description: Seconds banked from completed runs.
        timerStartedAt:
          type:
          - integer
          - 'null'
          description: Epoch ms when the current run started; null when stopped.
        billable:
          type: boolean
        lockedReason:
          type:
          - string
          - 'null'
          enum:
          - null
          - invoiced
          - approved
          - submitted
        version:
          type: integer
        deletedAt:
          type:
          - integer
          - 'null'
        createdAt:
          type: integer
        updatedAt:
          type: integer
      description: Live duration = accumulatedSeconds + (now − timerStartedAt)/1000 while running.
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      bearerFormat: ht_… personal API token