diff --git a/Project.toml b/Project.toml
index 2e0f3155f93ea45128b14386cfbb22b8ef1cb4d6..4c0d2b69af3f55b5139aaf52738bacee76d182dc 100644
--- a/Project.toml
+++ b/Project.toml
@@ -27,7 +27,7 @@ KM3ioKM3DBExt = "KM3DB"
 DocStringExtensions = "0.8, 0.9"
 HDF5 = "^0.16.15, ^0.17"
 KM3DB = "0.2.2"
-KM3NeTTestData = "^0.4.14"
+KM3NeTTestData = "^0.4.16"
 StaticArrays = "1"
 UnROOT = "^0.10.26"
 julia = "1"
diff --git a/docs/Project.toml b/docs/Project.toml
index 5b4aa2f413eb7f646b0a547236e3d84deb9077de..0fbf4f4a27b8124fce0d5f78bd9466b18a777297 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -12,4 +12,4 @@ PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925"
 [compat]
 Documenter = "1"
 FHist = "^0.11"
-KM3NeTTestData = "^0.4.15"
+KM3NeTTestData = "^0.4.16"
diff --git a/docs/src/api.md b/docs/src/api.md
index 6054714f9e04b75e1d6a84de468c127eb8df8803..6d85e8718e5ff43e1cc82e16f512c0c17b22433d 100644
--- a/docs/src/api.md
+++ b/docs/src/api.md
@@ -131,6 +131,7 @@ CHClient
 
 ### General tools
 ```@docs
+SummarysliceIntervalIterator
 getevent
 categorize
 nthbitset
diff --git a/src/exports.jl b/src/exports.jl
index bb7e003967cf3563b98a62ab7666f1549d7ad777..bad67b989490e2209a361e5635418e24cabdb689 100644
--- a/src/exports.jl
+++ b/src/exports.jl
@@ -110,8 +110,12 @@ hasshowerfit,
 hasshowerpositionfit,
 hasshowerprefit,
 
-# Utils
+# Tools
 MCEventMatcher,
+SummarysliceIntervalIterator,
+getevent,
+
+# Utils
 categorize,
 is3dmuon,
 is3dshower,
@@ -120,7 +124,6 @@ isnb,
 most_frequent,
 nthbitset,
 triggered,
-getevent,
 
 # Physics and math helpers
 CherenkovPhoton,
diff --git a/src/root/online.jl b/src/root/online.jl
index aa91483aa3559863a917bb7cd798c507a7e4ea70..e76c58b6a499a86dfb33e0a27c9fcc82c04a4446 100644
--- a/src/root/online.jl
+++ b/src/root/online.jl
@@ -113,6 +113,9 @@ struct Summaryslice
     header::SummarysliceHeader
     frames::Vector{SummaryFrame}
 end
+function Base.show(io::IO, s::Summaryslice)
+    print(io, "Summaryslice($(length(s.frames)) frames)")
+end
 struct SummarysliceContainer
     # For performance reasons we use directly the lazy types of UnROOT
     # We could also parametrise it.
diff --git a/src/tools/helpers.jl b/src/tools/helpers.jl
index d8149848e54d5d8c0dadc4d950fea7aeb4a25331..1681552d9873ff621bd125630c2306a280a6930c 100644
--- a/src/tools/helpers.jl
+++ b/src/tools/helpers.jl
@@ -58,3 +58,91 @@ function getevent(tree::T, frame_index, trigger_counter) where T<:Union{OnlineTr
 end
 getevent(tree::OfflineTree, idx) = tree[idx]
 getevent(tree::OnlineTree, idx) = tree.events[idx]
+
+
+"""
+
+An iterator which yields a `Vector{Summaryslice}` containing summaryslices of a given
+`time_interval` (in seconds). Useful when analysing summary data with fixed time intervals.
+The returned summaryslices are also sorted in time.
+
+# Examples
+```
+julia> f = ROOTFile("/Volumes/Ultraspeed/Data/Obelix/KM3NeT_00000133_00014728.root")
+ROOTFile{OnlineTree (83509 events, 106969 summaryslices)}
+
+julia> sii = SummarysliceIntervalIterator(f, 60)
+SummarysliceIntervalIterator (10753.1s, 60s intervals, 180 chunks)
+
+julia> for summaryslices in sii
+           @show length(summaryslices)
+           @show summaryslices[1].header
+           break
+       end
+length(summaryslices) = 440
+(summaryslices[1]).header = SummarysliceHeader(133, 14728, 134, UTCExtended(1676246413, 400000000, 0))
+```
+
+!!! note
+    Short time intervals (usually less than a few tens of seconds) will likely return empty
+    `Vector{Summaryslice}`s in the first few iterations due to a delay in run changes.
+    Additionally, the number of frames per summaryslice will gradually increase due
+    to the asynchronous nature of the run transition. See the example below with a time
+    inteval of 5s.
+
+```julia-repl
+julia> sii = SummarysliceIntervalIterator(f, 5)
+SummarysliceIntervalIterator (10753.1s, 5s intervals, 2151 chunks)
+
+julia> for summaryslices in sii
+           n = length(summaryslices)
+           @show n
+           n > 0 && break
+       end
+n = 0
+n = 0
+n = 5
+```
+
+"""
+struct SummarysliceIntervalIterator
+  sc::KM3io.SummarysliceContainer
+  time_interval::Int  # [s]
+  n_chunks::Int
+  timespan::Float64
+  indices::Vector{Int}
+  function SummarysliceIntervalIterator(f::ROOTFile, time_interval)
+    ss = f.online.summaryslices
+    sorted_summaryslice_indices = sortperm([sh.frame_index for sh in ss.headers])
+    timespan = ss.headers[sorted_summaryslice_indices[end]].frame_index / 10
+    n_chunks = Int(ceil(timespan / time_interval))
+    new(f.online.summaryslices, time_interval, n_chunks, timespan, sorted_summaryslice_indices)
+  end
+end
+function Base.show(io::IO, sii::SummarysliceIntervalIterator)
+    print(io, "SummarysliceIntervalIterator ($(sii.timespan)s, $(sii.time_interval)s intervals, $(sii.n_chunks) chunks)")
+end
+Base.eltype(::Type{SummarysliceIntervalIterator}) = Vector{KM3io.Summaryslice}
+Base.length(sii::SummarysliceIntervalIterator) = sii.n_chunks
+function Base.iterate(sii::SummarysliceIntervalIterator, state=(chunk_idx=1, s_idx=1))
+  state.chunk_idx > sii.n_chunks && return nothing
+
+  out_size = sii.time_interval * 10
+  out = empty!(Vector{KM3io.Summaryslice}(undef, out_size))
+
+  frame_index_upper = state.chunk_idx * sii.time_interval * 10
+
+  s_idx = state.s_idx
+  while s_idx <= length(sii.indices)
+    idx = sii.indices[s_idx]
+    summaryslice = sii.sc[idx]
+    if summaryslice.header.frame_index < frame_index_upper
+      push!(out, summaryslice)
+      s_idx += 1
+    else
+      break
+    end
+  end
+
+  return (out, (chunk_idx=state.chunk_idx+1, s_idx=s_idx))
+end
diff --git a/test/tools.jl b/test/tools.jl
index aff37baaa272fc7c4e8a26af91bff14af2f1b398..bb13ed89887d8f2d3edb9969f085422ba52c0384 100644
--- a/test/tools.jl
+++ b/test/tools.jl
@@ -336,3 +336,25 @@ end
     @test π/2 ≈ angle(Direction(1.,0,0), Direction(0.,0,1))
     @test π ≈ angle(Direction(1.,0,0), Direction(-1.,0,0))
 end
+
+
+@testset "SummarysliceIntervalIterator" begin
+    f = ROOTFile(datapath("online", "summaryslices.root"))
+
+    sii = SummarysliceIntervalIterator(f, 2)
+    suslis = collect(sii)
+    @test 55 == length(suslis)
+    @test 0 == length(suslis[1])
+    @test 1 == length(suslis[2])
+    @test 2 == length(suslis[3])
+    @test all(20 .== length.(suslis[4:end-5]))
+    @test 8 == length(suslis[end-1])
+    @test 6 == length(suslis[end])
+
+    flat_suslis = vcat(suslis...)
+    frame_indices = [s.header.frame_index for s in flat_suslis]
+    @test issorted(frame_indices)
+    @test frame_indices == sort([s.header.frame_index for s in f.online.summaryslices])
+
+    close(f)
+end