Not an answer but another approach using foldl/4 and the dict of SWI-Prolog.
Whenever I hear "scan through a list to perform a computation with a final result at the end", the appropriate approach is probably the "accumulator idiom". One hands a data structure (the "accumulator") between the calls where something happens with a list element, "accumulating" the result. foldl/N is meant to provide boilerplate code around this.
In this case the accumulator is the SWI_prolog dict accumulating "occurrence counts", which happens at each call to inc_for_key/3. At the end, we just need to select the entries with a high enough occurence count:
filter_occurrences(List,Limit,Reacheds,Finals) :-
foldl(inc_for_key,List,_{},Finals),
findall(Key,(Finals.Key >= Limit),Reacheds).
inc_for_key(Key,DictIn,DictOut) :-
(get_dict(Key,DictIn,X) -> succ(X,XP) ; XP=1),
put_dict(Key,DictIn,XP,DictOut).
Testing using plunit
:- begin_tests(filter_occurrences_less_than_n).
test("filter empty list",true(R == [])) :-
filter_occurrences([],3,R,_).
test("filter nonempty list #1 (limit 3)",true([R,Finals] == [[a,c],foo{a:4,b:2,c:3,d:1,e:1,f:1}])) :-
filter_occurrences([a,b,c,d,c,e,b,a,a,f,a,c],3,R,Finals),
dict_pairs(Finals,foo,_). % Sets the tag of the Finals dict to "foo"
test("filter nonempty list #2 (limit 4)",true([R,Finals] == [[a],foo{a:4,b:2,c:3,d:1,e:1,f:1}])) :-
filter_occurrences([a,b,c,d,c,e,b,a,a,f,a,c],4,R,Finals),
dict_pairs(Finals,foo,_). % Sets the tag of the Finals dict to "foo"
test("filter nonempty list #3 (limit 5)",true([R,Finals] == [[],foo{a:4,b:2,c:3,d:1,e:1,f:1}])) :-
filter_occurrences([a,b,c,d,c,e,b,a,a,f,a,c],5,R,Finals),
dict_pairs(Finals,foo,_). % Sets the tag of the Finals dict to "foo"
:- end_tests(filter_occurrences_less_than_n).
And so:
?- run_tests.
% PL-Unit: filter_occurrences_less_than_n .... done
% All 4 tests passed
true.