InfluxFlux
June 10, 2026 ยท View on GitHub
InfluxFlux.jl is a simple Julia package for querying InfluxDB, returning results as DataFrames using the functions or custom queries using the Flux query language. Only supports read access
Install
] add InfluxFlux
Usage
using InfluxFlux
api_token = "...."
srv = influx_server("https://some.influxdb.endpoint.influxdata.com", "some@organization.com", api_token)
Discovery helpers
buckets = list_buckets(srv)
measurements = list_measurements(srv, "example_bucket")
fields = list_fields(srv, "example_bucket")
fields = list_fields(srv, "example_bucket", "sensors")
Measurement helpers
InfluxDB groups data by tag set, so a measurement with multiple tag
combinations returns multiple tables. measurement() errors unless there is
exactly one; use measurement_multi() to get a Vector{DataFrame}, one per
table group.
using Dates
df = measurement(srv, "example_bucket", "sensors", now(UTC) - Hour(1), now())
tables = measurement_multi(srv, "example_bucket", "sensors", now(UTC) - Hour(1), now())
Using Time Zones
Time bounds accept a DateTime, ZonedDateTime, or a plain Int (epoch nanoseconds).
time_spec_to_epoc_ns() converts any of those to an integer if you need the value directly.
Some IANA timezone names are classified as legacy in TimeZones.jl and require passing
TimeZones.Class(:LEGACY) explicitly:
using Dates
using TimeZones
oslo = TimeZone("Europe/Oslo", TimeZones.Class(:LEGACY))
df = measurement(srv, "example_bucket", "sensors", ZonedDateTime(2025, 1, 1, oslo), ZonedDateTime(2025, 2, 1, oslo))
t_ns = time_spec_to_epoc_ns(now(UTC) - Hour(1))
Aggregate helpers
aggregate_measurement() downsamples using a Flux aggregateWindow. The window
argument is any Period (e.g. Second(30), Minute(1), Hour(6)). The fn keyword
defaults to "mean" but accepts any Flux aggregate: "min", "max", "sum",
"median", etc.
df = aggregate_measurement(srv, "example_bucket", "sensors", now(UTC) - Hour(1), now(), Minute(1))
tables = aggregate_measurement_multi(srv, "example_bucket", "sensors", now(UTC) - Hour(1), now(), Minute(1))
# custom aggregate function
df = aggregate_measurement(srv, "example_bucket", "sensors", now(UTC) - Day(1), now(), Hour(1); fn="max")
Raw queries
flux() returns the raw response body. flux_to_dataframe() parses it into a
DataFrame but errors if the query returns more than one table.
raw = flux(srv, "buckets()") |> String
table = flux_to_dataframe(srv, """
from(bucket: "example-bucket")
|> range(start: -1d)
|> filter(fn: (r) => r._field == "foo")
|> group(columns: ["sensorID"])
|> mean()
""")
flux_to_dataframe_multi() handles queries that yield multiple named result sets. Add |> yield(name: "foo") to your query for a meaningful key; without it the key defaults to :_result. Each key holds a Vector{DataFrame}.
tables = flux_to_dataframe_multi(srv, """
from(bucket: "example-bucket")
|> range(start: -1h)
|> yield(name: "example")
""")
tables.example
Tips for Writing Flux
Time column
measurement() and aggregate_measurement() map _time to UInt64 nanoseconds so it
arrives as a plain integer column. For raw Flux queries, add this yourself:
|> map(fn: (r) => ({ r with _time: uint(v: r._time) }))
When doing so, also consider calling clean_influx_df() to drop the InfluxDB internal
columns (result, table, _start, _stop, _measurement) that the high-level helpers
strip automatically:
df = clean_influx_df(flux_to_dataframe(srv, my_query))
Row order
Row order within a table is not guaranteed. Sort explicitly if needed:
sort!(df, :_time)
Consolidating multiple tables into one
To avoid the _multi variants entirely, collapse all series into a single table in Flux
using group() followed by sort(). This is useful when tag differences don't matter:
single = flux_to_dataframe(srv, """
from(bucket: "example-bucket")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "sensors")
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
|> group()
|> sort(columns: ["_time"])
""")