Add hour-of-day aggregation to the observations histogram endpoint

Platform(s), such as mobile, website, API, other:
API

URLs (aka web addresses) of any pages, if relevant:
GET /v1/observations/histogram (and the equivalent in v2)
Related discussion: https://forum.inaturalist.org/t/frequency-of-recording-the-same-species-is-it-useful/83436

Description of need:

The histogram endpoint supports interval=hour, which buckets by absolute clock-hour within the d1/d2 date range — useful for a short window, but it does not answer “at what hour of day does this species tend to appear, across its whole observation history.” Getting that today means paging through observation-level data (200 per request, capped at 10,000 without id_above) and bucketing client-side into 24 bins.

That per-observation approach is what I am currently doing in a small tool that charts hour-of-day activity for any project, place, user or species — the case that prompted this is camera trap data, where the hourly pattern is the actual signal (e.g. a species active only between 18h and 06h). For a single station with a few hundred records this is manageable, but it scales badly for anyone querying a large project or a long date range, and it is needlessly inefficient compared to server-side aggregation.

Feature request details:

Add an interval=hour_of_day option (naming open to whatever fits the existing convention) that aggregates by hour of day — 24 buckets, 00 through 23 — summed across the entire matched date range. This isn’t a new kind of behaviour: month_of_year already does exactly this. Querying /v1/observations/histogram?interval=month_of_year&place_id=6878 returns twelve integer-keyed buckets ({"1": 462801, "2": 375913, ...}) collapsed across every year of data for that place, not tied to a d1/d2 window. hour_of_day would be the same pattern applied to hours — 24 buckets instead of 12, same aggregation logic.

Two caveats raised in the linked discussion, flagged here for completeness rather than as objections: observed_time_zone accuracy on older observations is not fully trustworthy, and observations with no verified time tend to default to 00:00, which would show up as an artificial spike in the 00h bucket of any such aggregation. Both are data-quality issues independent of the endpoint itself, but worth being aware of when interpreting the output.

3 Likes