WristLog
All Help Topics

API

The WristLog API lets you build your own integrations. For example, a home automation that logs a wristcheck when you put a watch on, or a dashboard that shows which watches need some wrist time. It exposes your collection (with wear statistics) and lets you create wristchecks.

Authentication

Every request must include your personal API token as a bearer token:

Authorization: Bearer YOUR_API_TOKEN

Find and copy your token in Settings → API. You can regenerate it there at any time. Regenerating immediately invalidates the old token, so update your integrations afterwards. Keep the token secret: anyone who has it can read your collection and log wristchecks.

Base URL

All endpoints live under:

https://app.wristlog.com/api/v1

Responses are JSON.

Timezones

Every date and time in the API uses the timezone configured in your account settings, not UTC and not your client's local time. This applies consistently across every endpoint, so it's worth understanding:

  • Returned timestamps (like last_worn_at) carry your timezone's offset, e.g. 2026-06-14T08:30:00+02:00.
  • Day-based statistics (worn_today, days_since_last_worn, and the rolling 7/30-day windows) are calculated against the current date in your timezone.
  • Logging a wristcheck with no date uses the current moment in your timezone. A date-only value like 2026-06-14 is interpreted as that calendar day in your timezone.
  • The one-per-day rule (below) also uses your local day boundaries, so "today" means your today.

If a wristcheck seems to land on the wrong day, check that your account timezone in settings matches where you actually are.

List your watches

GET /api/v1/watches returns every watch you have logged (active, archived and sold) with wear statistics. There are no query parameters: every watch comes back, so use the archived and sold fields to filter in your own code if you only want what you currently wear.

curl https://app.wristlog.com/api/v1/watches \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Example response:

{
  "currency": "USD",
  "watches": [
    {
      "id": 42,
      "name": "Speedmaster",
      "manufacturer": "Omega",
      "model": "Moonwatch",
      "reference_number": "310.30.42.50.01.001",
      "movement_type": "Mechanical",
      "color": "#3380cc",
      "year": 2021,
      "purchase_date": "2021-06-01",
      "purchase_price": 6200.0,
      "sold": false,
      "sale_date": null,
      "sale_price": null,
      "image_url": "https://app.wristlog.com/rails/active_storage/representations/redirect/...",
      "archived": false,
      "total_wears": 128,
      "last_worn_at": "2026-06-14T08:30:00+02:00",
      "days_since_last_worn": 1,
      "worn_today": false,
      "wears_last_7_days": 3,
      "wears_last_30_days": 11,
      "service_cost": 740.0,
      "total_cost": 6940.0,
      "cost_per_wear": 54.22,
      "counted_in_cost_per_wear": true,
      "sale_result": null
    }
  ]
}

Fields explained:

  • image_url: link to the watch photo, or null if the watch has no photo.
  • total_wears: lifetime wristcheck count.
  • last_worn_at: ISO 8601 timestamp of the most recent wristcheck, or null if never worn.
  • days_since_last_worn: whole days since last worn, or null if never worn.
  • worn_today: whether the watch already has a wristcheck logged today.
  • wears_last_7_days / wears_last_30_days: wristchecks within those rolling windows.
  • sold: whether the watch has left your collection. Archiving does not make a watch sold.
  • sale_date: the day you sold it, or null if you still own it.
  • purchase_price / sale_price: what you paid and what you sold it for, or null if not set.

Money fields

Every watch list response carries a top-level currency with your account's ISO code, and every watch carries purchase_price and sale_price. If you also turned on money features in Settings, each watch gains the calculated figures below. While money features are off, those five keys are not in the response at all.

Amounts are plain numbers rounded to two decimals, with no currency symbol, so format them yourself using currency.

  • service_cost: the sum of the costs on the watch's service records, or null if none are recorded. Records logged in a different currency are left out.
  • total_cost: purchase price plus service cost plus the other costs logged in the watch's Papers tab (straps, batteries). null without a purchase price, since the total is unknown then rather than just the service spend.
  • cost_per_wear: total cost minus the sale price, divided by the lifetime wear count. It's null without a purchase price or with no wears, and it goes negative when the sale covered more than the watch cost you. Also null for a watch you left out of cost per wear.
  • counted_in_cost_per_wear: false when you left the watch out of cost per wear, from its Ownership details or the Money stats.
  • sale_result: sale price minus purchase price, so negative is a loss. null unless both prices are set. Service and other costs stay out of this one, they land in cost_per_wear.

Log a wristcheck

POST /api/v1/watches/:watch_id/wristchecks records that you wore a watch. By default the wristcheck is logged for the current moment (in your account's timezone).

curl -X POST https://app.wristlog.com/api/v1/watches/42/wristchecks \
  -H "Authorization: Bearer YOUR_API_TOKEN"

To backfill a different day, add an optional date (any value parseable as a date or datetime):

curl -X POST https://app.wristlog.com/api/v1/watches/42/wristchecks \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d "date=2026-06-14"

You can also tag the wear with optional occasions (free text, max 40 characters each, as many as you like). They feed the occasion charts on your stats page. A name you don't have yet is added to your occasion list in Settings, so send the names you actually use:

curl -X POST https://app.wristlog.com/api/v1/watches/42/wristchecks \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d "occasions[]=Travel" \
  -d "occasions[]=Formal"

The older single-valued occasion=Travel still works and is treated as one tag. A successful request returns 201 Created. occasions comes back in the order of your occasion list in Settings, not the order you sent, and is an empty array when the wear isn't tagged:

{ "id": 5012, "watch_id": 42, "log_date": "2026-06-14T00:00:00+02:00", "occasions": ["Formal", "Travel"] }

You can log only one wristcheck per watch per day, and you can't log a wristcheck in the future. Either case returns 422 with an explanation.

Errors & rate limits

Errors return a JSON body. Authentication and validation failures look like:

{ "error": "Invalid API token" }

{ "errors": ["You already Wristchecked this watch today"] }
  • 401 Unauthorized: missing or invalid token.
  • 404 Not Found: the watch doesn't exist in your collection.
  • 422 Unprocessable: validation failed (duplicate day, future date, unparseable date).
  • 429 Too Many Requests: you've exceeded the rate limit.

The API is limited to 60 requests per minute per token. That's plenty for personal automations, so design yours to poll on a sensible interval rather than in a tight loop.