Below is (an admittedly ugly) solution using a for loop. It checks each circle from 'buffpoints' for the total amount of area overlapped by other circles in 'buffpoints'.
There's likely a way to do this with purrr
, similar to this solution to a similar problem, but I couldn't quite get there. Hopefully someone else can.
library(sf)
library(tidyverse)
##### example data
set.seed(5)
poly <- st_sfc(st_polygon(list( cbind(c(0,100,100,0,0),c(0,0,100,100,0))
)))
points <- st_sample(poly, size = 20, type = "random" )
buffpoints <- st_buffer(points, dist = 10)
#####
# make buffpoints an sf object & add an id column
buff_sf <- buffpoints %>% st_as_sf() %>% mutate(id = row_number())
# for each row(polygon), find the area covered by the union of buff_sf, except for that row(polygon).
for(i in 1:nrow(buff_sf)){
covered_area <- buff_sf[i,] %>%
st_intersection(., st_union(buff_sf[-i,])) %>%
st_area()
#fix problem with numeric(0)
buff_sf[i, 'covered'] <- ifelse(length(covered_area) == 0, 0, covered_area)
}
#Add percent covered
buff_sf <- buff_sf %>%
mutate(area = st_area(.), coverage_pct = covered / area)
#Plotting to eyeball coverage, making break points at the coverage %
# you're looking for will help
ggplot(buff_sf) +
geom_sf(aes(fill = coverage_pct), alpha = .2) +
scale_fill_viridis_c()
Looks about right. Highly covered polygons are yellow, uncovered polygons are purple. The most covered polygon is at ~74%, 4 are covered 0% and by eye that looks to be the case.

Created on 2022-04-06 by the reprex package (v2.0.1)
>head(buff_sf)
#Simple feature collection with 6 features and 4 fields
#Geometry type: POLYGON
#Dimension: XY
#Bounding box: xmin: 0.4650128 ymin: 3.99837 xmax: 101.6876 ymax: 99.02071
#CRS: NA
# x id covered area coverage_pct
#1 POLYGON ((30.02145 89.02071... 1 104.5753253 314.0157 0.3330257463
#2 POLYGON ((78.52186 72.0701,... 2 0.0000000 314.0157 0.0000000000
#3 POLYGON ((101.6876 21.13403... 3 158.5424259 314.0157 0.5048868799
#4 POLYGON ((38.43995 22.57173... 4 195.9680109 314.0157 0.6240706676
#5 POLYGON ((20.46501 13.99837... 5 0.0968619 314.0157 0.0003084619
#6 POLYGON ((80.10575 47.99139... 6 54.2027628 314.0157 0.1726116124
And filtering based on % coverage:
buff_sf %>% filter(coverage_pct < .3)