Skip to contents

South Dakota enrolls 139,000 students across 150 school districts on the Great Plains – and the real story is what’s happening beneath the surface: suburbs doubling, cities shrinking, and high school enrollment surging while elementary stays flat.

Part of the njschooldata family of state education data packages.

Full documentation – all 15 stories with interactive charts, getting-started guide, and complete function reference.

Highlights

library(sdschooldata)
library(dplyr)
library(tidyr)
library(ggplot2)

theme_set(theme_minimal(base_size = 14))

enr <- fetch_enr_multi(c(2015:2020, 2022:2025), use_cache = TRUE)

1. Sioux Falls suburban growth outpaces the city

Harrisburg and Tea Area have seen explosive growth as Sioux Falls suburbs boom, with Harrisburg more than doubling its enrollment from 2,724 to 6,398 students in just 15 years.

suburbs <- fetch_enr_multi(c(2011, 2015, 2020, 2025), use_cache = TRUE)

suburb_trend <- suburbs |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         grepl("Harrisburg|Tea Area|Brandon Valley", district_name)) |>
  select(end_year, district_name, n_students)

stopifnot(nrow(suburb_trend) > 0)
suburb_trend
#>    end_year                 district_name n_students
#> 1      2011           Brandon Valley 49-2       3364
#> 2      2011               Harrisburg 41-2       2724
#> 3      2011 Tea Area School District 41-5       1383
#> 4      2015               Harrisburg 41-2       3900
#> 5      2015                 Tea Area 41-5       1610
#> 6      2015           Brandon Valley 49-2       3750
#> 7      2020           Brandon Valley 49-2       4682
#> 8      2020               Harrisburg 41-2       5449
#> 9      2020                 Tea Area 41-5       2045
#> 10     2025           Brandon Valley 49-2       5206
#> 11     2025               Harrisburg 41-2       6398
#> 12     2025                 Tea Area 41-5       2514
Suburban growth chart
Suburban growth chart

(source)

2. High school enrollment surging while elementary stays flat

South Dakota’s high school enrollment grew 12% from 2015 to 2025, while K-8 enrollment stayed essentially flat. This reflects larger birth cohorts aging into upper grades.

hs_elem <- enr |>
  filter(is_state, subgroup == "total_enrollment",
         grade_level %in% c("K", paste0("0", 1:8), "09", "10", "11", "12")) |>
  mutate(level = ifelse(grade_level %in% c("09", "10", "11", "12"), "High School", "K-8")) |>
  group_by(end_year, level) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop")

stopifnot(nrow(hs_elem) > 0)
hs_elem
#> # A tibble: 20 x 3
#>    end_year level       n_students
#>       <int> <chr>            <dbl>
#>  1     2015 High School      37100
#>  2     2015 K-8              93836
#>  3     2016 High School      37306
#>  4     2016 K-8              95214
#>  5     2017 High School      37625
#>  6     2017 K-8              96236
#>  7     2018 High School      37972
#>  8     2018 K-8              97021
#>  9     2019 High School      38825
#>  10    2019 K-8              97308
#>  11    2020 High School      40303
#>  12    2020 K-8              95681
#>  13    2022 High School      41804
#>  14    2022 K-8              96271
#>  15    2023 High School      42063
#>  16    2023 K-8              95696
#>  17    2024 High School      42133
#>  18    2024 K-8              95180
#>  19    2025 High School      41507
#>  20    2025 K-8              94070
K-8 vs High School chart
K-8 vs High School chart

(source)

3. Sioux Falls grows while Rapid City and rural hubs decline

The state’s largest district continues to grow, but Rapid City lost 12% of its enrollment since 2015 and other regional hubs are shrinking. District names vary across years in SD data, so we use district_id to track districts consistently.

urban_growth <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         grepl("Sioux Falls|Rapid City|Aberdeen|Brookings|Watertown", district_name)) |>
  group_by(district_id) |>
  arrange(end_year) |>
  summarize(
    district_name = last(district_name),
    y2015 = n_students[end_year == min(end_year)],
    y2025 = n_students[end_year == max(end_year)],
    pct_change = round((y2025 / y2015 - 1) * 100, 1),
    .groups = "drop"
  ) |>
  arrange(desc(pct_change))

stopifnot(nrow(urban_growth) > 0)
urban_growth
#> # A tibble: 5 x 5
#>   district_id district_name        y2015 y2025 pct_change
#>   <chr>       <chr>                <dbl> <dbl>      <dbl>
#> 1 05001       Brookings 05-1        3351  3483        3.9
#> 2 49005       Sioux Falls 49-5     24216 24841        2.6
#> 3 06001       Aberdeen 06-1         4485  4134       -7.8
#> 4 51004       Rapid City Area 51-4 13743 12040      -12.4
#> 5 14004       Watertown 14-4        4016  3425      -14.7
Urban growth chart
Urban growth chart

(source)

Data Taxonomy

Category Years Function Details
Enrollment 2006-2025 fetch_enr() / fetch_enr_multi() State, district, campus. Race, gender
Assessments Not yet available
Graduation Not yet available
Directory Current fetch_directory() District, school. Superintendent, principal, address, phone
Per-Pupil Spending Not yet available
Accountability Not yet available
Chronic Absence Not yet available
EL Progress Not yet available
Special Ed Not yet available

See the full data category taxonomy for what each category covers.

Quick Start

R

# install.packages("devtools")
devtools::install_github("almartin82/sdschooldata")
library(sdschooldata)
library(dplyr)

# Get 2025 enrollment data (2024-25 school year)
enr <- fetch_enr(2025)

# Statewide total
enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  pull(n_students)
#> 138,861

# Top 10 districts
enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  arrange(desc(n_students)) |>
  select(district_name, n_students) |>
  head(10)

# Get multiple years
enr_multi <- fetch_enr_multi(2020:2025)

Python

pip install git+https://github.com/almartin82/sdschooldata.git#subdirectory=pysdschooldata
import pysdschooldata as sd

# Fetch 2025 data (2024-25 school year)
enr = sd.fetch_enr(2025)

# Statewide total
total = enr[(enr['is_state'] == True) & (enr['subgroup'] == 'total_enrollment') & (enr['grade_level'] == 'TOTAL')]['n_students'].sum()
print(f"{total:,} students")
#> 138,861 students

# Get multiple years
enr_multi = sd.fetch_enr_multi([2020, 2021, 2022, 2023, 2024, 2025])

# Check available years
years = sd.get_available_years()
print(f"Data available: {years['min_year']}-{years['max_year']}")
#> Data available: 2006-2025

Explore More

Full analysis with 15 stories: - Enrollment trends – 15 stories covering statewide trends, demographics, regional dynamics, and more - Function reference

Data Notes

Data Source

All data comes from the South Dakota Department of Education Fall Census.

Available Years

2006-2025 (20 years)

Era Years Format Notes
Era 0 2006-2010 Excel (.xls) Legacy XLS format
Era 1 2011-2012 Excel (.xlsx) District-level only
Era 2 2013-2020 Excel (.xlsx) Added school-level demographics
Era 3 2021-2025 Excel (.xlsx) 4-digit year format

Census Day

South Dakota collects enrollment data on Census Day (typically the last Friday in September).

Suppression Rules

  • Small counts may be suppressed to protect student privacy
  • Suppressed values appear as NA in the data
  • Check for NA values when analyzing small subgroups or small districts

What’s Included

  • Levels: State, district, and campus
  • Demographics: White, Black, Hispanic, Asian, Native American, Pacific Islander, Multiracial (campus-level only, 2022+)
  • Grade levels: Pre-K, K, 1-12, Ungraded
  • Note: Demographic subgroups are reported at the campus level and must be aggregated for district/state totals

What’s NOT Available

  • Gender (male/female) data
  • Economic disadvantage status (separate data)
  • Special education enrollment (separate system)
  • English Learner counts (separate file)
  • Pre-2006 data (PDFs only)

Deeper Dive

4. South Dakota enrollment is slowly growing

Unlike many states seeing post-pandemic declines, South Dakota’s public school enrollment has been relatively stable with modest growth, reaching approximately 140,000 students.

state_totals <- enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  select(end_year, n_students) |>
  mutate(change = n_students - lag(n_students),
         pct_change = round(change / lag(n_students) * 100, 2))

stopifnot(nrow(state_totals) > 0)
state_totals
#>    end_year n_students change pct_change
#> 1      2015     134054     NA         NA
#> 2      2016     135811   1757       1.31
#> 3      2017     137251   1440       1.06
#> 4      2018     138428   1177       0.86
#> 5      2019     139442   1014       0.73
#> 6      2020     139154   -288      -0.21
#> 7      2022     141429   2275       1.63
#> 8      2023     141005   -424      -0.30
#> 9      2024     140587   -418      -0.30
#> 10     2025     138861  -1726      -1.23
Statewide enrollment trend
Statewide enrollment trend

(source)

5. Sioux Falls dominates the state

The Sioux Falls School District is by far the largest in the state, with more students than the next several districts combined. Rapid City is a distant second.

enr_2025 <- fetch_enr(2025, use_cache = TRUE)

top_10 <- enr_2025 |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  arrange(desc(n_students)) |>
  head(10) |>
  select(district_name, n_students)

stopifnot(nrow(top_10) > 0)
top_10
#>           district_name n_students
#> 1      Sioux Falls 49-5      24841
#> 2  Rapid City Area 51-4      12040
#> 3       Harrisburg 41-2       6398
#> 4   Brandon Valley 49-2       5206
#> 5         Aberdeen 06-1       4134
#> 6        Brookings 05-1       3483
#> 7        Watertown 14-4       3425
#> 8            Huron 02-2       3042
#> 9          Yankton 63-3       2973
#> 10           Meade 46-1       2957
Top 10 districts
Top 10 districts

(source)

6. Native American students are a significant population

South Dakota has one of the highest percentages of Native American students in the nation, reflecting the state’s large reservation lands including Pine Ridge, Rosebud, and Standing Rock. Demographic data is reported at the campus level; here we aggregate across all campuses statewide.

state_total <- enr_2025 |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  pull(n_students)

demographics <- enr_2025 |>
  filter(is_campus, grade_level == "TOTAL",
         subgroup %in% c("white", "native_american", "hispanic", "black", "asian", "multiracial")) |>
  group_by(subgroup) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop") |>
  mutate(pct = round(n_students / state_total * 100, 1)) |>
  arrange(desc(n_students))

stopifnot(nrow(demographics) > 0)
demographics
#> # A tibble: 6 x 3
#>   subgroup        n_students   pct
#>   <chr>                <dbl> <dbl>
#> 1 white                95447  68.7
#> 2 native_american      14283  10.3
#> 3 hispanic             12845   9.3
#> 4 multiracial           8681   6.3
#> 5 black                 5051   3.6
#> 6 asian                 2308   1.7
Demographics chart
Demographics chart

(source)

7. Many tiny rural districts

South Dakota has a large number of very small school districts, many with fewer than 200 students, reflecting the state’s rural character and sparse population.

small <- enr_2025 |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  filter(n_students < 200) |>
  arrange(n_students) |>
  head(15) |>
  select(district_name, n_students)

stopifnot(nrow(small) > 0)
small
#>         district_name n_students
#> 1   Elk Mountain 16-2         20
#> 2         Bowdle 22-1         45
#> 3  South Central 26-5         52
#> 4          Hoven 53-2        101
#> 5       Edgemont 23-1        106
#> 6       Oelrichs 23-3        117
#> 7          Bison 52-1        118
#> 8     White Lake 01-3        122
#> 9       McIntosh 15-1        141
#> 10        Doland 56-2        146
#> 11        Colome 59-3        153
#> 12       Herreid 10-1        153
#> 13         Henry 14-2        154
#> 14       Wakpala 15-3        159
#> 15 Tripp-Delmont 33-5        160

(source)

8. Hispanic enrollment is rising fast

Hispanic students are the fastest-growing demographic group in South Dakota schools, climbing from 3.98% to 4.63% of campus enrollment in just four years (2022-2025). Campus-level demographic data is available starting in 2022.

hispanic_years <- c(2022:2025)
enr_hispanic <- fetch_enr_multi(hispanic_years, use_cache = TRUE)

hispanic_trend <- enr_hispanic |>
  filter(is_campus, subgroup == "hispanic", grade_level == "TOTAL") |>
  group_by(end_year) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop")

hispanic_totals <- enr_hispanic |>
  filter(is_campus, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  group_by(end_year) |>
  summarize(total = sum(n_students, na.rm = TRUE), .groups = "drop")

hispanic_trend <- hispanic_trend |>
  left_join(hispanic_totals, by = "end_year") |>
  mutate(pct = round(n_students / total * 100, 2)) |>
  select(end_year, n_students, pct)

stopifnot(nrow(hispanic_trend) > 0)
hispanic_trend
#> # A tibble: 4 x 3
#>   end_year n_students   pct
#>      <int>      <dbl> <dbl>
#> 1     2022      11265  3.98
#> 2     2023      11983  4.25
#> 3     2024      12751  4.53
#> 4     2025      12845  4.63
Hispanic growth chart
Hispanic growth chart

(source)

9. Rapid City: West River anchor

Rapid City Area School District anchors western South Dakota, serving as the only major urban district west of the Missouri River.

rapid <- fetch_enr_multi(c(2015:2020, 2022:2025), use_cache = TRUE)

rapid_trend <- rapid |>
  filter(is_district, grepl("Rapid City", district_name),
         subgroup == "total_enrollment", grade_level == "TOTAL") |>
  select(end_year, n_students)

stopifnot(nrow(rapid_trend) > 0)
rapid_trend
#>    end_year n_students
#> 1      2015      13743
#> 2      2016      13743
#> 3      2017      13760
#> 4      2018      13832
#> 5      2019      13609
#> 6      2020      12809
#> 7      2022      12743
#> 8      2023      12433
#> 9      2024      12313
#> 10     2025      12040
Rapid City chart
Rapid City chart

(source)

10. Reservation schools: Todd County and Pine Ridge

Districts serving reservation communities face unique challenges. Todd County (Rosebud) and Oglala Lakota County (Pine Ridge) serve predominantly Native American students.

reservation <- fetch_enr_multi(c(2015, 2020, 2025), use_cache = TRUE)

res_data <- reservation |>
  filter(is_district,
         grepl("Todd County|Oglala Lakota|Shannon", district_name),
         subgroup == "total_enrollment",
         grade_level == "TOTAL")

stopifnot(nrow(res_data) > 0)
res_data
#>   end_year     type district_id campus_id             district_name campus_name
#> 1     2015 District       65001      <NA> Oglala Lakota County 65-1        <NA>
#> 2     2015 District       66001      <NA>          Todd County 66-1        <NA>
#> 3     2020 District       65001      <NA>        Oglala Lakota 65-1        <NA>
#> 4     2020 District       66001      <NA>          Todd County 66-1        <NA>
#> 5     2025 District       65001      <NA> Oglala Lakota County 65-1        <NA>
#> 6     2025 District       66001      <NA>          Todd County 66-1        <NA>
#>   grade_level         subgroup n_students pct is_state is_district is_campus
#> 1       TOTAL total_enrollment       1532   1    FALSE        TRUE     FALSE
#> 2       TOTAL total_enrollment       2013   1    FALSE        TRUE     FALSE
#> 3       TOTAL total_enrollment       1811   1    FALSE        TRUE     FALSE
#> 4       TOTAL total_enrollment       2156   1    FALSE        TRUE     FALSE
#> 5       TOTAL total_enrollment       1706   1    FALSE        TRUE     FALSE
#> 6       TOTAL total_enrollment       1956   1    FALSE        TRUE     FALSE
#>   is_public district_type_code district_type_name
#> 1      TRUE               <NA>               <NA>
#> 2      TRUE               <NA>               <NA>
#> 3      TRUE               <NA>               <NA>
#> 4      TRUE               <NA>               <NA>
#> 5      TRUE                 10        10 - Public
#> 6      TRUE                 10        10 - Public
Reservation schools chart
Reservation schools chart

(source)

11. Rural consolidation pressure

Many of South Dakota’s smallest districts face consolidation pressure. Districts with fewer than 100 students struggle with economies of scale.

tiny <- fetch_enr(2025, use_cache = TRUE)

tiny_districts <- tiny |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         n_students < 100) |>
  arrange(n_students) |>
  head(20) |>
  select(district_name, n_students)

stopifnot(nrow(tiny_districts) > 0)
tiny_districts
#>        district_name n_students
#> 1  Elk Mountain 16-2         20
#> 2        Bowdle 22-1         45
#> 3 South Central 26-5         52
Rural distribution chart
Rural distribution chart

(source)

12. The Black Hills corridor

The Black Hills region forms a distinct educational corridor, with Rapid City at its center and smaller communities like Spearfish, Sturgis, and Custer serving surrounding areas.

black_hills <- fetch_enr(2025, use_cache = TRUE)

bh_districts <- black_hills |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         grepl("Rapid City|Spearfish|Sturgis|Custer|Lead|Deadwood|Belle Fourche", district_name)) |>
  arrange(desc(n_students)) |>
  select(district_name, n_students)

stopifnot(nrow(bh_districts) > 0)
bh_districts
#>          district_name n_students
#> 1 Rapid City Area 51-4      12040
#> 2       Spearfish 40-2       2301
#> 3   Belle Fourche 09-1       1241
#> 4          Custer 16-1        854
#> 5   Lead-Deadwood 40-1        590
Black Hills chart
Black Hills chart

(source)

13. Aberdeen and the northeast

Aberdeen School District anchors the northeast, with surrounding agricultural communities feeding into regional schools.

northeast <- fetch_enr_multi(c(2015:2020, 2022:2025), use_cache = TRUE)

ne_trend <- northeast |>
  filter(is_district, grepl("Aberdeen", district_name),
         subgroup == "total_enrollment", grade_level == "TOTAL") |>
  select(end_year, n_students)

stopifnot(nrow(ne_trend) > 0)
ne_trend
#>    end_year n_students
#> 1      2015       4485
#> 2      2016       4554
#> 3      2017       4517
#> 4      2018       4471
#> 5      2019       4483
#> 6      2020       4477
#> 7      2022       4326
#> 8      2023       4265
#> 9      2024       4237
#> 10     2025       4134
Northeast chart
Northeast chart

(source)

14. Pre-K enrollment patterns

Pre-kindergarten enrollment varies significantly across districts, reflecting different local policies and access to early childhood programs.

prek <- fetch_enr(2025, use_cache = TRUE)

prek_data <- prek |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "PK") |>
  filter(n_students > 0) |>
  arrange(desc(n_students)) |>
  head(15) |>
  select(district_name, n_students)

stopifnot(nrow(prek_data) > 0)
prek_data
#>                district_name n_students
#> 1           Sioux Falls 49-5        791
#> 2               Yankton 63-3        196
#> 3       Rapid City Area 51-4        165
#> 4      Wagner Community 11-4        105
#> 5  Oglala Lakota County 65-1         79
#> 6                Lennox 41-4         59
#> 7             Watertown 14-4         59
#> 8                Hamlin 28-3         55
#> 9               Douglas 51-1         53
#> 10       Brandon Valley 49-2         51
#> 11           Harrisburg 41-2         43
#> 12       McCook Central 43-7         43
#> 13            Brookings 05-1         40
#> 14      Alcester-Hudson 61-1         37
#> 15            Garretson 49-4         36
Pre-K chart
Pre-K chart

(source)

15. Multiracial students: South Dakota’s growing diversity

Multiracial students grew from 8,129 (2.87%) to 8,681 (3.13%) of campus enrollment between 2022 and 2025, reflecting changing family patterns statewide. Campus-level demographic data is available starting in 2022.

multi <- fetch_enr_multi(c(2022, 2023, 2024, 2025), use_cache = TRUE)

multi_trend <- multi |>
  filter(is_campus, subgroup == "multiracial", grade_level == "TOTAL") |>
  group_by(end_year) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop")

multi_totals <- multi |>
  filter(is_campus, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  group_by(end_year) |>
  summarize(total = sum(n_students, na.rm = TRUE), .groups = "drop")

multi_trend <- multi_trend |>
  left_join(multi_totals, by = "end_year") |>
  mutate(pct = round(n_students / total * 100, 2)) |>
  select(end_year, n_students, pct)

stopifnot(nrow(multi_trend) > 0)
multi_trend
#> # A tibble: 4 x 3
#>   end_year n_students   pct
#>      <int>      <dbl> <dbl>
#> 1     2022       8129  2.87
#> 2     2023       8370  2.97
#> 3     2024       8576  3.05
#> 4     2025       8681  3.13
Multiracial chart
Multiracial chart

(source)