Skip to content
Miru 3.0 is here — expenses, CLI, dark mode, and 6 report types. Read the announcement →
API Engineering

Building on the Miru API: What Developers Need to Know

Authentication, endpoints, rate limits, and real code examples. Everything you need to build on top of Miru's REST API.

Vipul A M · · 4 min read

Miru has a REST API. Not a “REST API” in scare quotes where half the endpoints are undocumented and the other half return HTML error pages. An actual, consistent, JSON-in-JSON-out API that covers every resource in the system.

Here’s what you need to know to start building on it.


Authentication

Bearer token. Generate one from Settings > Profile > API Token in the Miru web app. Include it in every request:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://app.miru.so/api/v1/time_entries

Tokens don’t expire on their own. Revoke them from the same settings page. If you’re building a multi-user integration, each user generates their own token. There’s no OAuth flow — by design. OAuth is the right choice for third-party apps with millions of users. For internal tools and integrations, a bearer token is simpler, faster, and harder to get wrong.


Base URL

https://app.miru.so/api/v1

Self-hosted instances use your own domain: https://miru.yourcompany.com/api/v1. Everything else works the same.


Endpoints

The API is organized around resources. Each resource supports the standard CRUD operations you’d expect:

ResourceMethodsWhat It Does
/time_entriesGET, POST, PUT, DELETETrack and manage time
/invoicesGET, POST, PUTCreate and manage invoices
/clientsGET, POST, PUT, DELETEManage clients
/projectsGET, POST, PUT, DELETEManage projects
/expensesGET, POSTTrack expenses
/paymentsGETView payment history
/usersGETList team members

GET requests support pagination and filtering. POST and PUT expect JSON bodies. DELETE does what you’d expect. Every response includes standard HTTP status codes — 200 for success, 201 for created, 422 for validation errors, 404 for not found.


Real Examples

Create a time entry:

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project_id": 42, "duration": 120, "note": "API integration work"}' \
  https://app.miru.so/api/v1/time_entries

List invoices for a specific client:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://app.miru.so/api/v1/invoices?client_id=7"

Create a new project:

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Q2 Website Redesign", "client_id": 7, "billable": true}' \
  https://app.miru.so/api/v1/projects

Export time entries for a date range:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://app.miru.so/api/v1/time_entries?from=2026-01-01&to=2026-01-31" \
  | jq '.[] | {date: .work_date, hours: .duration, project: .project_name}'

Pipe it to jq, csvkit, or whatever you like. The output is JSON. Do what you want with it.


Rate Limits

100 requests per minute per token. If you exceed it, you’ll get a 429 response with a Retry-After header. Back off and retry.

For most integrations, this is more than enough. If you’re syncing thousands of records, batch your reads and space your writes. If you’re polling for changes, consider caching the last result and only fetching new records. The API returns records in chronological order, so you can use timestamps to pick up where you left off.


What People Build

In the months since the API launched, we’ve seen people build:

  • Slack bots that let team members log time without leaving the chat
  • Dashboard integrations that pull Miru data into Grafana and Metabase
  • Billing automation scripts that generate invoices on the first of every month
  • Calendar sync tools that create time entries from Google Calendar events
  • CI/CD hooks that log deployment time to the associated project
  • Mobile shortcuts using iOS Shortcuts and the API to log time with one tap

The API is the same one the Miru web app uses. If you can do it in the browser, you can do it with curl.


Error Handling

Errors return JSON with a message field and a standard HTTP status code. No HTML error pages. No stack traces in production. No guessing.

{
  "error": "Validation failed",
  "messages": ["Duration must be greater than 0", "Project is required"]
}

Handle 422s (validation), 401s (bad token), 404s (resource not found), and 429s (rate limited). That covers 99% of error scenarios.


Getting Started

  1. Sign up at app.miru.so or self-host
  2. Generate an API token from Settings > Profile
  3. Make your first request: curl -H "Authorization: Bearer TOKEN" https://app.miru.so/api/v1/users
  4. Build something

The full API reference with request/response schemas is at docs.miru.so. But honestly, the best way to learn any API is to open a terminal and start making requests. The endpoints are predictable. The responses are consistent. You’ll figure it out in ten minutes.

Your tools should talk to each other. The API makes that possible. Get your token and start building.

Share:
VA

Vipul A M

Co-founder at Saeloun. Building Miru. Rails contributor. Shipping from Pune, India.

Try Miru today

Free to start. No credit card required.

Start Tracking Free