Alabama’s 717,473 public school students across 153 districts and 1,362 campuses – 5 years of enrollment data (2021-2025) from the Alabama State Department of Education.
Part of the njschooldata family.
Full documentation – all 15 stories with interactive charts, getting-started guide, and complete function reference.
Highlights
library(alschooldata)
library(dplyr)
library(tidyr)
library(ggplot2)
theme_set(theme_minimal(base_size = 14))
enr <- fetch_enr_multi(2021:2025, use_cache = TRUE)
enr_2025 <- enr |> filter(end_year == 2025)1. Virtual schools exploded to 20,000 students
Alabama’s virtual schools grew from 12,741 students (8 schools) in 2021 to 19,963 students (16 schools) in 2025. Alabama Connections Academy alone enrolls 7,339, making Limestone County appear to be one of the state’s largest districts.
virtual <- enr |>
filter(is_campus, subgroup == "total_enrollment", grade_level == "TOTAL",
grepl("Virtual|Connections|Destinations", campus_name, ignore.case = TRUE)) |>
group_by(end_year) |>
summarize(n_schools = n(), total_students = sum(n_students), .groups = "drop") |>
arrange(end_year)
stopifnot(nrow(virtual) > 0)
virtual
#> end_year n_schools total_students
#> 1 2021 8 12741
#> 2 2022 13 15856
#> 3 2023 14 15138
#> 4 2024 13 16207
#> 5 2025 16 19963
2. Multiracial students are the fastest-growing demographic
Students identifying as multiracial grew from 25,456 (3.5%) to 42,305 (5.9%) since 2021 – a 66% increase. At this pace, multiracial students will outnumber Asian and Native American students combined.
multi <- enr |>
filter(is_state, subgroup == "multiracial", grade_level == "TOTAL") |>
mutate(pct = round(pct * 100, 1)) |>
select(end_year, n_students, pct) |>
arrange(end_year)
stopifnot(nrow(multi) > 0)
multi
#> end_year n_students pct
#> 1 2021 25456 3.5
#> 2 2022 29716 4.0
#> 3 2023 33651 4.6
#> 4 2024 37946 5.3
#> 5 2025 42305 5.9
3. Black Belt counties are hemorrhaging students
Perry County has lost 32% of its enrollment since 2021 – the steepest decline of any Alabama district with 500+ students. Sumter County lost 29%, and Selma City lost 24%.
decline <- enr |>
filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
end_year %in% c(2021, 2025)) |>
select(end_year, district_name, n_students) |>
pivot_wider(names_from = end_year, values_from = n_students) |>
filter(!is.na(`2021`), !is.na(`2025`), `2021` >= 500) |>
mutate(change = `2025` - `2021`,
pct_change = round((`2025` / `2021` - 1) * 100, 1)) |>
arrange(pct_change) |>
head(10)
stopifnot(nrow(decline) > 0)
decline
#> district_name 2021 2025 change pct_change
#> 1 Perry County 1148 778 -370 -32.2
#> 2 Sumter County 1202 851 -351 -29.2
#> 3 Talladega City 1837 1356 -481 -26.2
#> 4 Selma City 2776 2124 -652 -23.5
#> 5 Dallas County 2775 2211 -564 -20.3
#> 6 Wilcox County 1404 1127 -277 -19.7
#> 7 Conecuh County 1474 1197 -277 -18.8
#> 8 Lowndes County 1311 1081 -230 -17.5
#> 9 Anniston City 1980 1643 -337 -17.0
#> 10 Roanoke City 1475 1239 -236 -16.0
Data Taxonomy
| Category | Years | Function | Details |
|---|---|---|---|
| Enrollment | 2021-2025 |
fetch_enr() / fetch_enr_multi()
|
State, district, campus. Race, gender, FRPL, SpEd, LEP |
| Assessments | – | – | Not yet available |
| Graduation | – | – | Not yet available |
| Directory | Current | fetch_directory() |
District, school. Principal, superintendent, address, phone, website, grades |
| 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("remotes")
remotes::install_github("almartin82/alschooldata")
library(alschooldata)
library(dplyr)
# Fetch one year
enr_2025 <- fetch_enr(2025)
# Fetch multiple years
enr_multi <- fetch_enr_multi(2021:2025)
# State totals
enr_2025 %>%
filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL")
# District breakdown
enr_2025 %>%
filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL") %>%
arrange(desc(n_students))
# Demographics
enr_2025 %>%
filter(is_state, grade_level == "TOTAL",
subgroup %in% c("white", "black", "hispanic", "asian")) %>%
select(subgroup, n_students, pct)Python
import pyalschooldata as al
# Fetch one year
enr_2025 = al.fetch_enr(2025)
# Fetch multiple years
enr_multi = al.fetch_enr_multi([2021, 2022, 2023, 2024, 2025])
# State totals
enr_2025[
(enr_2025['is_state'] == True) &
(enr_2025['subgroup'] == 'total_enrollment') &
(enr_2025['grade_level'] == 'TOTAL')
]
# District breakdown
district_df = enr_2025[
(enr_2025['is_district'] == True) &
(enr_2025['subgroup'] == 'total_enrollment') &
(enr_2025['grade_level'] == 'TOTAL')
].sort_values('n_students', ascending=False)
# Demographics
enr_2025[
(enr_2025['is_state'] == True) &
(enr_2025['grade_level'] == 'TOTAL') &
(enr_2025['subgroup'].isin(['white', 'black', 'hispanic', 'asian']))
][['subgroup', 'n_students', 'pct']]Explore More
Full analysis with 15 stories: - Enrollment trends – 15 stories - Function reference
Data Notes
Data Source
Alabama State Department of Education (ALSDE) Federal Report Card: alsde.edu
What’s Included
- Levels: State, district (~153), campus (~1,362)
- Demographics: White, Black, Hispanic, Asian, Native American, Pacific Islander, Multiracial
- Special populations: Economically disadvantaged, English learners, Students with disabilities
- Gender: Male, Female
- Grade levels: PK-12
Alabama ID System
- District codes: 3 digits (county systems, city systems, and charter operators)
- Campus codes: 4 digits unique within each district
Data Quality Notes
- Enrollment counts are as of Census Day (typically early October)
- Economically disadvantaged rate shows volatility (48%-65%) likely due to reporting methodology changes
- Special education rate spiked 2022-2024, possibly reflecting expanded identification
- Virtual school enrollment is reported under the hosting district, inflating some district totals
Deeper Dive
4. Alabama lost 12,000 students since 2022
Statewide enrollment peaked at 735,808 in 2022 and has since declined by over 18,000 students. The 2024 drop of 11,073 was the steepest single-year decline.
state_totals <- enr |>
filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
select(end_year, n_students) |>
arrange(end_year) |>
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 2021 729786 NA NA
#> 2 2022 735808 6022 0.83
#> 3 2023 729789 -6019 -0.82
#> 4 2024 718716 -11073 -1.52
#> 5 2025 717473 -1243 -0.17
5. Hispanic enrollment surged from 9.5% to 12.2% in four years
Hispanic students are the fastest-growing racial/ethnic group in Alabama schools, adding nearly 19,000 students since 2021 while overall enrollment declined.
hispanic <- enr |>
filter(is_state, subgroup == "hispanic", grade_level == "TOTAL") |>
mutate(pct = round(pct * 100, 1)) |>
select(end_year, n_students, pct) |>
arrange(end_year)
stopifnot(nrow(hispanic) > 0)
hispanic
#> end_year n_students pct
#> 1 2021 69093 9.5
#> 2 2022 74561 10.1
#> 3 2023 78638 10.8
#> 4 2024 84661 11.8
#> 5 2025 87790 12.2
6. Mobile County is Alabama’s largest system – and shrinking fastest
Mobile County Public Schools serves 47,366 students, nearly 14,000 more than second-place Jefferson County. But Mobile has lost 5,768 students since 2021, a decline of 10.9%.
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 Mobile County 47366
#> 2 Jefferson County 33844
#> 3 Baldwin County 30491
#> 4 Montgomery County 25491
#> 5 Huntsville City 22776
#> 6 Shelby County 20159
#> 7 Madison County 19769
#> 8 Birmingham City 19710
#> 9 Tuscaloosa County 18135
#> 10 Limestone County 15816
7. Alabama’s student body: 56% white, 32% Black, 12% Hispanic
Alabama’s racial demographics are shifting. White students still form the majority but their share dropped from 58.3% to 56.1% since 2021, while Hispanic and multiracial shares both grew.
demographics <- enr_2025 |>
filter(is_state, grade_level == "TOTAL",
subgroup %in% c("white", "black", "hispanic", "asian",
"native_american", "pacific_islander", "multiracial")) |>
mutate(pct = round(pct * 100, 1)) |>
select(subgroup, n_students, pct) |>
arrange(desc(n_students))
stopifnot(nrow(demographics) > 0)
demographics
#> subgroup n_students pct
#> 1 white 402422 56.1
#> 2 black 228708 31.9
#> 3 hispanic 87790 12.2
#> 4 multiracial 42305 5.9
#> 5 native_american 30582 4.3
#> 6 asian 11511 1.6
#> 7 pacific_islander 1945 0.3
8. Nearly 3 in 5 Alabama students are economically disadvantaged
The economically disadvantaged rate jumped from 51% in 2021 to 65% in 2023, then settled at 59% in 2025. This volatility may reflect changes in reporting methodology rather than true poverty shifts.
econ <- enr |>
filter(is_state, grade_level == "TOTAL", subgroup == "econ_disadv") |>
mutate(pct = round(pct * 100, 1)) |>
select(end_year, n_students, pct) |>
arrange(end_year)
stopifnot(nrow(econ) > 0)
econ
#> end_year n_students pct
#> 1 2021 371737 50.9
#> 2 2022 351049 47.7
#> 3 2023 477329 65.4
#> 4 2024 465245 64.7
#> 5 2025 422645 58.9
9. Birmingham City lost 2,191 students in four years
Birmingham City Schools has shed students every single year since 2021, dropping from 21,901 to 19,710 – a 10% decline. Once a top-5 Alabama district, it now ranks 8th.
bham <- enr |>
filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
grepl("Birmingham City", district_name)) |>
select(end_year, n_students) |>
arrange(end_year) |>
mutate(change = n_students - lag(n_students))
stopifnot(nrow(bham) > 0)
bham
#> end_year n_students change
#> 1 2021 21901 NA
#> 2 2022 21163 -738
#> 3 2023 19921 -1242
#> 4 2024 19829 -92
#> 5 2025 19710 -119
10. English Learners grew 52% in four years
Alabama’s English Learner population surged from 33,595 (4.6%) to 51,068 (7.1%), closely tracking the growth in Hispanic enrollment and reflecting the state’s demographic transformation.
el_trend <- enr |>
filter(is_state, subgroup == "lep", grade_level == "TOTAL") |>
mutate(pct = round(pct * 100, 1)) |>
select(end_year, n_students, pct) |>
arrange(end_year)
stopifnot(nrow(el_trend) > 0)
el_trend
#> end_year n_students pct
#> 1 2021 33595 4.6
#> 2 2022 36956 5.0
#> 3 2023 41430 5.7
#> 4 2024 47838 6.7
#> 5 2025 51068 7.1
11. Special Ed spiked to 18% then fell back to 14.5%
Special education enrollment jumped from 14% to 18% between 2021 and 2024, then dropped to 14.5% in 2025. The spike may reflect expanded eligibility or pandemic-related identification, with the recent decline suggesting a correction.
sped <- enr |>
filter(is_state, subgroup == "special_ed", grade_level == "TOTAL") |>
mutate(pct = round(pct * 100, 1)) |>
select(end_year, n_students, pct) |>
arrange(end_year)
stopifnot(nrow(sped) > 0)
sped
#> end_year n_students pct
#> 1 2021 102117 14.0
#> 2 2022 130946 17.8
#> 3 2023 130655 17.9
#> 4 2024 129379 18.0
#> 5 2025 104371 14.5
12. 3rd grade is the largest class in Alabama
With 57,054 students, 3rd grade edges out 1st grade (56,798) and kindergarten (55,467) as the most enrolled grade level in 2025.
grade_dist <- enr_2025 |>
filter(is_state, subgroup == "total_enrollment",
grade_level %in% c("K", "01", "02", "03", "04", "05",
"06", "07", "08", "09", "10", "11", "12")) |>
mutate(grade_level = factor(grade_level,
levels = c("K", "01", "02", "03", "04", "05",
"06", "07", "08", "09", "10", "11", "12"))) |>
select(grade_level, n_students) |>
arrange(grade_level)
stopifnot(nrow(grade_dist) > 0)
grade_dist
#> grade_level n_students
#> 1 K 55467
#> 2 01 56798
#> 3 02 56515
#> 4 03 57054
#> 5 04 54307
#> 6 05 53653
#> 7 06 53450
#> 8 07 54573
#> 9 08 55053
#> 10 09 56624
#> 11 10 55541
#> 12 11 53178
#> 13 12 50839
13. Mobile County: 73% low-income, 51% Black
Mobile County’s demographics are dramatically different from statewide averages. Its economically disadvantaged rate (73%) is 14 points above the state average, and its Black student population (51%) is 19 points above the statewide share.
mobile_demo <- enr_2025 |>
filter(is_district, grade_level == "TOTAL",
grepl("Mobile County", district_name),
subgroup %in% c("white", "black", "hispanic",
"econ_disadv", "total_enrollment")) |>
mutate(pct = round(pct * 100, 1)) |>
select(subgroup, n_students, pct) |>
arrange(desc(n_students))
stopifnot(nrow(mobile_demo) > 0)
mobile_demo
#> subgroup n_students pct
#> 1 total_enrollment 47366 100.0
#> 2 econ_disadv 34569 73.0
#> 3 black 24070 50.8
#> 4 white 17525 37.0
#> 5 hispanic 3262 6.914. Charter schools are Alabama’s smallest districts
Alabama’s charter sector is small but growing. The smallest 10 districts by enrollment are dominated by charter and specialty schools, with Freedom Prep Academy enrolling just 36 students.
smallest <- enr_2025 |>
filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL") |>
arrange(n_students) |>
head(10) |>
select(district_name, n_students)
stopifnot(nrow(smallest) > 0)
smallest
#> district_name n_students
#> 1 Freedom Prep Academy 36
#> 2 Alabama Aerospace and Aviation 146
#> 3 Floretta P. Carson Visual and Performing Arts 193
#> 4 Covenant Academy of Mobile 302
#> 5 Magic City Acceptance Academy 307
#> 6 Breakthrough Charter School 359
#> 7 Empower Schools of Alabama 375
#> 8 Linden City 380
#> 9 LIFE Academy 497
#> 10 Legacy Prep 501
15. Middle school enrollment is shrinking while elementary grows
Elementary (K-5) and high school (9-12) enrollment both grew since 2021, but middle school (6-8) dropped from 173,548 to 163,076 – a loss of over 10,000 students.
grade_bands <- enr |>
filter(is_state, subgroup == "total_enrollment",
grade_level %in% c("K", "01", "02", "03", "04", "05",
"06", "07", "08",
"09", "10", "11", "12")) |>
mutate(band = case_when(
grade_level %in% c("K", "01", "02", "03", "04", "05") ~ "Elementary (K-5)",
grade_level %in% c("06", "07", "08") ~ "Middle (6-8)",
grade_level %in% c("09", "10", "11", "12") ~ "High School (9-12)"
)) |>
group_by(end_year, band) |>
summarize(n_students = sum(n_students), .groups = "drop")
stopifnot(nrow(grade_bands) > 0)
grade_bands
#> end_year band n_students
#> 1 2021 Elementary (K-5) 327017
#> 2 2021 High School (9-12) 211750
#> 3 2021 Middle (6-8) 173548
#> 4 2022 Elementary (K-5) 329002
#> 5 2022 High School (9-12) 212460
#> 6 2022 Middle (6-8) 171009
#> 7 2023 Elementary (K-5) 330453
#> 8 2023 High School (9-12) 213639
#> 9 2023 Middle (6-8) 167263
#> 10 2024 Elementary (K-5) 332704
#> 11 2024 High School (9-12) 216257
#> 12 2024 Middle (6-8) 165208
#> 13 2025 Elementary (K-5) 333794
#> 14 2025 High School (9-12) 216182
#> 15 2025 Middle (6-8) 163076