A Beeminder client for Emacs
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Beeminder client for Emacs

A Beeminder client for Emacs.


Beeminder is a self-motivation web service. It is rather clever, and I should not waste time trying to explain it here – go to their website to learn more about it.

However, their default web interface sucks, and while the official Android app is a lot better, it still has a few drawbacks. The biggest one is that it is not integrated with Emacs. This Elisp library fixes that problem (and a few others, like lack of goal sorting and filtering).

This library is still under development, and everything (including keybindings) may change.


Put beeminder.el somewhere Emacs can load it and (require 'beeminder). Try M-x customize-group RET Beeminder RET. Two things you have to set up are: your Beeminder username (beeminder-username) and authorization token (beeminder-auth-token – sign in and head to https://www.beeminder.com/api/v1/auth_token.json to retrieve it.) The option beeminder-goal-pp-format is responsible for how each goal is displayed; customize it to change the look of the list.

You need to install packages request and anaphora (e.g. using the built-in Emacs package manager) in order to use beeminder.el.


The entry point is M-x beeminder-list-goals. After a while, you should see your goals. Press h to see what’s available. Refresh the list with C-l. Reload your goals with g. Quit with q. Most commands are self-explanatory (and if not, their docstrings explain what they do). Note: the dates are calculated with the assumption that hours after midnight, but before 6:00am, still belong to today! See the option beeminder-when-the-day-ends for how to change that.


Currently, you can sort goals according to their “losedate” (derailment time) or “midnight” setting. These two variants are enabled by the functions beeminder-sort-by-losedate (bound to l) and beeminder-sort-by-midnight (bound to m).


The main drawback of the official clients is that you can’t easily hide goals which are of no interest for you at the moment. With Emacs, you can. Filtering commands are located on prefix f.

You can filter goals by derailment time (NUMBER-OF-DAYS f d, number of days defaults to 3), by how much time is left to the goal’s “midnight” (HOURS f u, defaults to 8), or by how much percent of daily rate you did today (PERCENTAGE f t, defaults to 100). What “today” means is governed by options beeminder-when-the-day-ends (which see) and beeminder-use-goal-midnight-today-values (which see). TL;DR: with the default setting, “today” is the stretch of time between 6:00am and 5:59am the next day. With these options, you can change that hour or make the notion of “today” depend on the goal’s “midnight” setting.

You can also set a zero or negative argument to any filtering command. Try it to see what happens; beeminder.el tries to do the right thing for any filter.

(Currently, you can use just d, u and t keybindings for filtering by derailment time, urgency and percentage of today’s work done. However, this may change in the future.)

You can also “kill” individual goals, i.e., make them invisible, with f k. (This is also bound to C-k for convenience). You can “unkill” all killed goals with f y (or C-y), or show (in the minibuffer) which goals are killed with C-u f y.

If you’re like me, many of your goals are of the “do this every day” category. I usually set the rate for such goals to 0.8 daily, and enter 1.0 each day, and set the “max safe days” to e.g. 3 (of course, you need Plan Bee for that). This way, I have a bit of leeway – I can safely slip once every six days. On the other hand, if one of these goals derails in, say, 2 days, it can easily get lost, especially when filtering out all goals with due date e.g. later than tomorrow. In order to avoid that, you can set the variable beeminder-everyday-goals-list in your init.el to a list of slugs of “everyday” goals (as symbols). These goals will be shown even if their deadline is later than the derailment time filter setting. You may toggle displaying them by pressing e.

You might also want to save current filter settings for later retrieval. This can be done with f s. Saved goals can be retrieved by f r. While saving is not persistent across Emacs sessions, you can (ab)use this feature to have your favorite filter settings enabled for retrieval in init.el by defining the variable beeminder-saved-filters. For instance, to be able to quickly retrieve the goals which are derailing today, with the exception of two of them, you can put this in your init.el:

(setq beeminder-saved-filters '((killed uvi meta)
                                (losedate . 0)))

You can disable all filters with f c. (This also saves the current filters if no filter settings were saved previously.) Alternatively, you can disable a particular filter with - KEY (that is, minus sign and the key which enables that filter).

Submitting datapoints

Move the point to a goal and press RET to submit a datapoint. If you give a prefix argument, this will be the amount; if not, you will be asked for it (default is 1). Prefix argument - (a minus sign) submits the datapoint with yesterday’s date. Prefix argument C-u asks for a date. (Having Org-mode loaded works better, since then org-read-date is used. If you want to use this functionality – and believe me, you do – and you happen not to use Org-mode, you can (require 'org) in your init.el. You don’t have to install anything, Org is shipped with Emacs.) After submitting, the goal is dirty, i.e., there is a discrepancy between its state on the server and in the client. Dirty goals are shown in italic and gray. Refresh the goal list (by pressing g) to “clean” them (it might require from a few seconds to a few minutes to work, probably because of Beeminder’s server overload).

It may happen that the goals which should lose their dirtiness do not do it. (One situation when it can happen is when you submit a datapoint of 0.) In such cases, you can call M-x beeminder-clear-dirty-goals to manually reset the “dirty” flag for all goals.


Many actions (submitting goals, reloading goals, deleting or editing datapoints) are logged. Press L (beeminder-pop-log) to see the log and q to exit it.

Viewing goal details

You can press TAB with point on a goal to display more detailed information about a goal in a separate window. This information includes (by default) most data available in the API, plus a set of recent datapoints. The user option beeminder-goal-template holds the template for displaying that; it is a string with embedded keywords (or s-expressions) starting with the # sign. The list of supported keywords together with the way they should be interpreted is kept in beeminder-goal-template-fields-alist; any keyword not present there is assumed to be a property of the goal datastructure (see https://www.beeminder.com/api#goal for details). You may also embed arbitrary s-exps in the template (preceding them also with #).

The option beeminder-history-length determines how many datapoints are downloaded from the server. Its default value is 7, which means a week’s worth of them. Pressing m downloads more datapoints (with a positive prefix argument, it downloads that many more days’ worth of datapoints; with a negative prefix argument, it downloads datapoints from number of days equal to the abolute value of the argument; with prefix argument equal to zero, it downloads all datapoints; without a prefix argument, it downloads datapoints from beeminder-history-length more days than displayed currently.)

You can press q or TAB again to quit the goal details window. Pressing n and p will move you to the next and previous datapoint (or N datapoints forward/backward with a prefix argument; notice that you don’t need to press C-u to enter prefix arguments here, too).

You can also press e to edit the current datapoint. You will be asked about the timestamp (again, using org-read-date if available), the value and the comment. In all three cases, the default is the previous value; for the comment, you can also use the usual minibuffer history commands like M-n, M-p or M-r (see the node on Minibuffer history in the Emacs manual). Pressing C-g at any moment cancels the editing.

Pressing d deletes the current datapoint. Emacs will ask for confirmation; use the option beeminder-confirm-datapoint-deletion to change this behavior.

Note that editing a datapoint does not mark the goal as dirty; the current design of dirtiness makes it rather hard to fix. Deleting a datapoint works properly in this regard.

There is (rather experimental) support for displaying graphs. Press i to download and view the graph for the current goal.

Pressing W opens the current goal in a browser.

Org-mode integration

beeminder.el supports two kinds of Org-mode integration: submitting data on marking items as DONE or on clocking out. Both use Org properties to set various things up. Org-mode integration can be toggled with the beeminder-org-integration-mode command; as the name suggests, it is a (global) minor mode. You may also turn the two features on or off independently by evaluating the functions beeminder-org-done-submitting or beeminder-org-clock-out-submitting, with a positive or a nonpositive argument respectively.

Then, for each item you want to link to a Beeminder goal, set its beeminder property to done or clock, and its slug property to the goal slug. You might also want to set the beeminder-org-inherit-beeminder-properties option to t to turn property inheritance on for Beeminder-related stuff. (This is probably most useful for clocking subtasks.)

If for some reason you want to confirm the submitting each time, you may set the comment property to ask. Then, you will be asked for a comment each time. Other possible values for the comment property are: time (you will get a comment of the form via Org mode at <time>), headline, which uses the headline as the comment, path, which uses the headline together with the whole path, and any other string, which is then used as a comment. In the latter case, you can use a few special markers in that string: %t (ISO-8601 timestamp), %h (headline), %p (headline with the path), %% (literal “%” sign). In the case of the absence of the comment property, beeminder-org-default-comment is used.

Marking items as DONE

Marking an Org heading as DONE can automatically submit a Beeminder datapoint. For that, set the property beeminder to done and put the goal slug in the slug property. The amount of the datapoint will be 1, though this can be overriden by setting the property amount to a number.

This feature probably makes the most sense for items scheduled with a repeater.

Submitting time for clocked items

Another way of leveraging Org-mode’s features is submitting time of clocked items for “do X for at least Y minutes”-type goals. For that, set the beeminder property to clock and the slug property to the goal slug. Each time this particular item is clocked out, the number of minutes is submitted as a Beeminder datapoint. Alternatively, you may set the unit property to hours so that the value is divided by 60.

Since it may happen that you clock out some item when offline, you may also place point at a particular clock line and trigger the submission manually by M-x beeminder-org-submit-clock-at-point.

If you clocked more items while you were offline, you may find the command M-x beeminder-org-submit-all-clocks useful. It submits all clocks in the region (if the region is active) or in the current subtree otherwise. For performance reasons, it submits only clocks that ended less than beeminder-org-submit-all-clocks-default-minutes minutes ago (by default 24 · 60 = 1440 minutes). This value can be also changed using a numeric prefix argument.

Note that calling beeminder-org-submit-clock-at-point multiple times on the same clock line submits it only once. More precisely, the idempotency key is constructed from the start and end times of the clock item.

Planned features

  • More sorting/filtering options (per request).
  • Displaying goal graphs.
  • Loading more datapoints for selected goal.
  • More statistics for a goal.
  • Road editing (much less likely to be done).

Bug reports/feature requests

Feel free to send bug reports and/or feature requests to me.